aboutsummaryrefslogtreecommitdiff
path: root/src/arm
diff options
context:
space:
mode:
authorLRN <lrn1986@gmail.com>2013-03-13 17:49:26 +0000
committerLRN <lrn1986@gmail.com>2013-03-13 17:49:26 +0000
commit405f776bc08486af4edb80e18149c0829732b347 (patch)
treed5fc635a51641dec6b53cb2540276f34ae8f6210 /src/arm
parent3ceae682287492ecc768aea5c4c463216a35774d (diff)
downloadgnunet-405f776bc08486af4edb80e18149c0829732b347.tar.gz
gnunet-405f776bc08486af4edb80e18149c0829732b347.zip
All-encompassing ARM update
Diffstat (limited to 'src/arm')
-rw-r--r--src/arm/Makefile.am2
-rw-r--r--src/arm/arm.h53
-rw-r--r--src/arm/arm_api.c1130
-rw-r--r--src/arm/gnunet-arm.c521
-rw-r--r--src/arm/gnunet-service-arm.c526
-rw-r--r--src/arm/mockup-service.c14
-rw-r--r--src/arm/test_arm_api.c151
-rw-r--r--src/arm/test_exponential_backoff.c265
-rw-r--r--src/arm/test_gnunet_service_arm.c81
9 files changed, 1665 insertions, 1078 deletions
diff --git a/src/arm/Makefile.am b/src/arm/Makefile.am
index 48d9955b8..dcc0f125f 100644
--- a/src/arm/Makefile.am
+++ b/src/arm/Makefile.am
@@ -19,7 +19,7 @@ endif
19lib_LTLIBRARIES = libgnunetarm.la 19lib_LTLIBRARIES = libgnunetarm.la
20 20
21libgnunetarm_la_SOURCES = \ 21libgnunetarm_la_SOURCES = \
22 arm_api.c arm.h 22 arm_api.c arm_monitor_api.c arm.h
23libgnunetarm_la_LIBADD = \ 23libgnunetarm_la_LIBADD = \
24 $(top_builddir)/src/util/libgnunetutil.la \ 24 $(top_builddir)/src/util/libgnunetutil.la \
25 $(GN_LIBINTL) $(XLIB) 25 $(GN_LIBINTL) $(XLIB)
diff --git a/src/arm/arm.h b/src/arm/arm.h
index 21884107c..aad16fd71 100644
--- a/src/arm/arm.h
+++ b/src/arm/arm.h
@@ -36,20 +36,60 @@
36GNUNET_NETWORK_STRUCT_BEGIN 36GNUNET_NETWORK_STRUCT_BEGIN
37 37
38/** 38/**
39 * Reply from ARM to client. 39 * Status update from ARM to client.
40 */ 40 */
41struct GNUNET_ARM_ResultMessage 41struct GNUNET_ARM_StatusMessage
42{ 42{
43 43
44 /** 44 /**
45 * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT. 45 * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_STATUS.
46 */ 46 */
47 struct GNUNET_MessageHeader header; 47 struct GNUNET_MessageHeader header;
48 48
49 /** 49 /**
50 * Status from the 'enum GNUNET_ARM_ProcessStatus' 50 * Status from the 'enum GNUNET_ARM_ServiceStatus'
51 */ 51 */
52 uint32_t status; 52 uint32_t status;
53
54 /* followed by a 0-terminated service name */
55};
56
57struct GNUNET_ARM_Message
58{
59 /**
60 * Reply to client, type is GNUNET_MESSAGE_TYPE_ARM_RESULT or
61 * GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT.
62 * OR
63 * Request from client, type is GNUNET_MESSAGE_TYPE_ARM_REQUEST
64 */
65 struct GNUNET_MessageHeader header;
66
67 /**
68 * ID of a request that is being replied to.
69 * OR
70 * ID of a request that is being sent.
71 */
72 uint64_t request_id;
73
74 /* For requests - followed by a 0-terminated service name */
75};
76
77
78/**
79 * Reply from ARM to client.
80 */
81struct GNUNET_ARM_ResultMessage
82{
83
84 /**
85 * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_RESULT, with an ID.
86 */
87 struct GNUNET_ARM_Message arm_msg;
88
89 /**
90 * Result from the 'enum GNUNET_ARM_Result'
91 */
92 uint32_t result;
53}; 93};
54 94
55/** 95/**
@@ -61,9 +101,10 @@ struct GNUNET_ARM_ResultMessage
61struct GNUNET_ARM_ListResultMessage 101struct GNUNET_ARM_ListResultMessage
62{ 102{
63 /** 103 /**
64 * Reply to client is of type GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT 104 * Reply to client, of type is GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT,
105 * with an ID.
65 */ 106 */
66 struct GNUNET_MessageHeader header; 107 struct GNUNET_ARM_Message arm_msg;
67 108
68 /** 109 /**
69 * Number of '\0' terminated strings that follow 110 * Number of '\0' terminated strings that follow
diff --git a/src/arm/arm_api.c b/src/arm/arm_api.c
index aee9afb24..d434ff3af 100644
--- a/src/arm/arm_api.c
+++ b/src/arm/arm_api.c
@@ -1,6 +1,6 @@
1/* 1/*
2 This file is part of GNUnet. 2 This file is part of GNUnet.
3 (C) 2009, 2010, 2012 Christian Grothoff (and other contributing authors) 3 (C) 2009, 2010, 2012, 2013 Christian Grothoff (and other contributing authors)
4 4
5 GNUnet is free software; you can redistribute it and/or modify 5 GNUnet is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published 6 it under the terms of the GNU General Public License as published
@@ -21,7 +21,7 @@
21/** 21/**
22 * @file arm/arm_api.c 22 * @file arm/arm_api.c
23 * @brief API for accessing the ARM service 23 * @brief API for accessing the ARM service
24 * @author Christian Grothoff 24 * @author Christian Grothoff, LRN
25 */ 25 */
26#include "platform.h" 26#include "platform.h"
27#include "gnunet_arm_service.h" 27#include "gnunet_arm_service.h"
@@ -38,7 +38,7 @@ struct GNUNET_ARM_Handle
38{ 38{
39 39
40 /** 40 /**
41 * Our connection to the ARM service. 41 * Our control connection to the ARM service.
42 */ 42 */
43 struct GNUNET_CLIENT_Connection *client; 43 struct GNUNET_CLIENT_Connection *client;
44 44
@@ -47,257 +47,469 @@ struct GNUNET_ARM_Handle
47 */ 47 */
48 struct GNUNET_CONFIGURATION_Handle *cfg; 48 struct GNUNET_CONFIGURATION_Handle *cfg;
49 49
50 /**
51 * Handle for our current transmission request.
52 */
53 struct GNUNET_CLIENT_TransmitHandle *cth;
54
55 /**
56 * Head of doubly-linked list of pending requests.
57 */
58 struct ARMControlMessage *control_pending_head;
59
60 /**
61 * Tail of doubly-linked list of pending requests.
62 */
63 struct ARMControlMessage *control_pending_tail;
64
65 /**
66 * Head of doubly-linked list of sent requests.
67 */
68 struct ARMControlMessage *control_sent_head;
69
70 /**
71 * Tail of doubly-linked list of sent requests.
72 */
73 struct ARMControlMessage *control_sent_tail;
74
75 /**
76 * ID of the reconnect task (if any).
77 */
78 GNUNET_SCHEDULER_TaskIdentifier reconnect_task;
79
80 /**
81 * Current delay we use for re-trying to connect to core.
82 */
83 struct GNUNET_TIME_Relative retry_backoff;
84
85 /**
86 * Are we currently disconnected and hence unable to send?
87 */
88 unsigned char currently_down;
89
90 /**
91 * Callback to invoke on connection/disconnection.
92 */
93 GNUNET_ARM_ConnectionStatusCallback conn_status;
94
95 /**
96 * Closure for conn_status.
97 */
98 void *conn_status_cls;
99
100 /**
101 * GNUNET_YES if we're running a service test.
102 */
103 unsigned char service_test_is_active;
104
105 /**
106 * Counter for request identifiers
107 */
108 uint64_t request_id_counter;
50}; 109};
51 110
111
52/** 112/**
53 * Context for handling the shutdown of a service. 113 * Entry in a doubly-linked list of control messages to be transmitted
114 * to the arm service.
115 *
116 * The actual message is allocated at the end of this struct.
54 */ 117 */
55struct ShutdownContext 118struct ARMControlMessage
56{ 119{
57 /** 120 /**
58 * Connection to the service that is being shutdown. 121 * This is a doubly-linked list.
59 */ 122 */
60 struct GNUNET_CLIENT_Connection *sock; 123 struct ARMControlMessage *next;
61 124
62 /** 125 /**
63 * Time allowed for shutdown to happen. 126 * This is a doubly-linked list.
64 */ 127 */
65 struct GNUNET_TIME_Absolute timeout; 128 struct ARMControlMessage *prev;
66 129
67 /** 130 /**
68 * Task set up to cancel the shutdown request on timeout. 131 * Callback for service state change requests.
69 */ 132 */
70 GNUNET_SCHEDULER_TaskIdentifier cancel_task; 133 GNUNET_ARM_ResultCallback result_cont;
71 134
72 /** 135 /**
73 * Task to call once shutdown complete 136 * Callback for service list requests.
74 */ 137 */
75 GNUNET_CLIENT_ShutdownTask cont; 138 GNUNET_ARM_ServiceListCallback list_cont;
76 139
77 /** 140 /**
78 * Closure for shutdown continuation 141 * Closure for 'result_cont' or 'list_cont'.
79 */ 142 */
80 void *cont_cls; 143 void *cont_cls;
81 144
82 /** 145 /**
83 * Handle for transmission request. 146 * Timeout for the operation.
84 */ 147 */
85 struct GNUNET_CLIENT_TransmitHandle *th; 148 struct GNUNET_TIME_Absolute timeout;
86 149
150 /**
151 * Type of the request expressed as a message type (start, stop or list).
152 */
153 uint16_t type;
154
155 /**
156 * Flags for passing std descriptors to ARM (when starting ARM).
157 */
158 enum GNUNET_OS_InheritStdioFlags std_inheritance;
159
160 /**
161 * ARM handle.
162 */
163 struct GNUNET_ARM_Handle *h;
164
165 /**
166 * Message to send.
167 */
168 struct GNUNET_ARM_Message *msg;
169
170 /**
171 * Task to run when request times out.
172 */
173 GNUNET_SCHEDULER_TaskIdentifier timeout_task_id;
87}; 174};
88 175
176static void
177client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg);
178
179static void
180reconnect_arm (struct GNUNET_ARM_Handle *h);
181
182static void
183trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down);
184
89 185
90/** 186/**
91 * Handler receiving response to service shutdown requests. 187 * Task scheduled to try to re-connect to arm.
92 * First call with NULL: service misbehaving, or something.
93 * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK:
94 * - service will shutdown
95 * Second call with NULL:
96 * - service has now really shut down.
97 * 188 *
98 * @param cls closure 189 * @param cls the 'struct GNUNET_ARM_Handle'
99 * @param msg NULL, indicating socket closure. 190 * @param tc task context
100 */ 191 */
101static void 192static void
102service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg) 193reconnect_arm_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
194{
195 struct GNUNET_ARM_Handle *h = cls;
196
197 h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
198 LOG (GNUNET_ERROR_TYPE_DEBUG, "Connecting to ARM service after delay\n");
199 reconnect_arm (h);
200}
201
202
203static void
204clear_pending_messages (struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus result)
103{ 205{
104 struct ShutdownContext *shutdown_ctx = cls; 206 struct ARMControlMessage *cm;
105 207
106 if (NULL != msg) 208 LOG (GNUNET_ERROR_TYPE_DEBUG,
209 "Clearing pending messages\n");
210
211 while (NULL != (cm = h->control_pending_head))
107 { 212 {
108 /* We just expected a disconnect! Report the error and be done with it... */ 213 GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
109 GNUNET_break (0); 214 h->control_pending_tail, cm);
110 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); 215 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
111 GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task); 216 GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
112 GNUNET_CLIENT_disconnect (shutdown_ctx->sock); 217 if (NULL != cm->result_cont)
113 GNUNET_free (shutdown_ctx); 218 cm->result_cont (cm->cont_cls, cm->h, result, NULL, 0);
114 return; 219 GNUNET_free_non_null (cm->msg);
220 GNUNET_free (cm);
115 } 221 }
116 if (NULL != shutdown_ctx->cont)
117 /* shutdown is now complete, as we waited for the network disconnect... */
118 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_DOWN);
119 GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
120 GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
121 GNUNET_free (shutdown_ctx);
122} 222}
123 223
124
125/** 224/**
126 * Shutting down took too long, cancel receive and return error. 225 * Close down any existing connection to the ARM service and
226 * try re-establishing it later.
127 * 227 *
128 * @param cls closure 228 * @param h our handle
129 * @param tc context information (why was this task triggered now)
130 */ 229 */
131static void 230static void
132service_shutdown_cancel (void *cls, 231reconnect_arm_later (struct GNUNET_ARM_Handle *h)
133 const struct GNUNET_SCHEDULER_TaskContext *tc)
134{ 232{
135 struct ShutdownContext *shutdown_ctx = cls; 233 if (GNUNET_NO != h->currently_down)
234 return;
136 235
137 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT); 236 if (NULL != h->cth)
138 GNUNET_CLIENT_disconnect (shutdown_ctx->sock); 237 {
139 GNUNET_free (shutdown_ctx); 238 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
140} 239 h->cth = NULL;
240 }
241
242 if (NULL != h->client)
243 {
244 GNUNET_CLIENT_disconnect (h->client);
245 h->client = NULL;
246 }
247
248 if (NULL != h->conn_status)
249 h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_NO);
250
251 h->currently_down = GNUNET_YES;
141 252
253 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
254 h->reconnect_task =
255 GNUNET_SCHEDULER_add_delayed (h->retry_backoff, &reconnect_arm_task, h);
256 /* Don't clear pending messages on disconnection, deliver them later
257 clear_pending_messages (h, GNUNET_ARM_REQUEST_DISCONNECTED);
258 GNUNET_assert (NULL == h->control_pending_head);
259 */
260 h->retry_backoff = GNUNET_TIME_STD_BACKOFF (h->retry_backoff);
261}
142 262
143/** 263/**
144 * If possible, write a shutdown message to the target 264 * Transmit the next message to the arm service.
145 * buffer and destroy the client connection.
146 * 265 *
147 * @param cls the "struct GNUNET_CLIENT_Connection" to destroy 266 * @param cls closure with the 'struct GNUNET_ARM_Handle'
148 * @param size number of bytes available in buf 267 * @param size number of bytes available in buf
149 * @param buf NULL on error, otherwise target buffer 268 * @param buf where the callee should write the message
150 * @return number of bytes written to buf 269 * @return number of bytes written to buf
151 */ 270 */
152static size_t 271static size_t
153write_shutdown (void *cls, size_t size, void *buf) 272transmit_arm_message (void *cls, size_t size, void *buf)
154{ 273{
155 struct ShutdownContext *shutdown_ctx = cls; 274 struct GNUNET_ARM_Handle *h = cls;
156 struct GNUNET_MessageHeader *msg; 275 struct ARMControlMessage *cm;
276 struct GNUNET_ARM_Message *arm_msg;
277 uint16_t msize;
278 uint64_t request_id;
157 279
158 shutdown_ctx->th = NULL; 280 LOG (GNUNET_ERROR_TYPE_DEBUG,
159 if (size < sizeof (struct GNUNET_MessageHeader)) 281 "transmit_arm_message is running with %p buffer of size %lu. ARM is known to be %s\n",
282 buf, size, h->currently_down ? "unconnected" : "connected");
283 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK == h->reconnect_task);
284 h->cth = NULL;
285 if ((GNUNET_YES == h->currently_down) && (NULL != buf))
286 {
287 h->currently_down = GNUNET_NO;
288 if (NULL != h->conn_status)
289 h->conn_status (h->conn_status_cls, h, GNUNET_YES, GNUNET_NO);
290 h->retry_backoff = GNUNET_TIME_UNIT_MILLISECONDS;
291 GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
292 GNUNET_TIME_UNIT_FOREVER_REL);
293 }
294 if (NULL == buf)
295 {
296 LOG (GNUNET_ERROR_TYPE_DEBUG,
297 "Transmission failed, initiating reconnect\n");
298 reconnect_arm_later (h);
299 return 0;
300 }
301 if (NULL == (cm = h->control_pending_head))
160 { 302 {
161 LOG (GNUNET_ERROR_TYPE_WARNING, 303 LOG (GNUNET_ERROR_TYPE_DEBUG, "Queue is empty, not sending anything\n");
162 _("Failed to transmit shutdown request to client.\n")); 304 return 0;
163 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
164 GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
165 GNUNET_free (shutdown_ctx);
166 return 0; /* client disconnected */
167 } 305 }
168 GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, 306
169 shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); 307 GNUNET_assert (NULL != cm->msg);
170 shutdown_ctx->cancel_task = 308 msize = ntohs (cm->msg->header.size);
171 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining 309 if (size < msize)
172 (shutdown_ctx->timeout), 310 {
173 &service_shutdown_cancel, shutdown_ctx); 311 LOG (GNUNET_ERROR_TYPE_DEBUG,
174 msg = (struct GNUNET_MessageHeader *) buf; 312 "Request is too big (%u < %u), not sending it\n", size, msize);
175 msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN); 313 trigger_next_request (h, GNUNET_NO);
176 msg->size = htons (sizeof (struct GNUNET_MessageHeader)); 314 return 0;
177 return sizeof (struct GNUNET_MessageHeader); 315 }
316 arm_msg = cm->msg;
317 if (0 == h->request_id_counter)
318 h->request_id_counter++;
319 request_id = h->request_id_counter++;
320 LOG (GNUNET_ERROR_TYPE_DEBUG,
321 "Transmitting control message with %u bytes of type %u to arm with id %llu\n",
322 (unsigned int) msize, (unsigned int) ntohs (cm->msg->header.type), request_id);
323 arm_msg->request_id = GNUNET_htonll (request_id);
324 memcpy (buf, cm->msg, msize);
325 /* Otherwise we won't be able to find it later! */
326 arm_msg->request_id = request_id;
327
328 GNUNET_CONTAINER_DLL_remove (h->control_pending_head,
329 h->control_pending_tail, cm);
330 GNUNET_CONTAINER_DLL_insert_tail (h->control_sent_head,
331 h->control_sent_tail, cm);
332
333 /* Don't free msg, keep it around (kind of wasteful, but then we don't
334 * really have many messages to handle, and it'll be freed when it times
335 * out anyway.
336 */
337 trigger_next_request (h, GNUNET_NO);
338 return msize;
178} 339}
179 340
180 341
181/** 342/**
182 * Request that the service should shutdown. 343 * Check the list of pending requests, send the next
183 * Afterwards, the connection will automatically be 344 * one to the arm.
184 * disconnected. Hence the "sock" should not
185 * be used by the caller after this call
186 * (calling this function frees "sock" after a while).
187 * 345 *
188 * @param sock the socket connected to the service 346 * @param h arm handle
189 * @param timeout how long to wait before giving up on transmission 347 * @param ignore_currently_down transmit message even if not initialized?
190 * @param cont continuation to call once the service is really down 348 */
191 * @param cont_cls closure for continuation 349static void
350trigger_next_request (struct GNUNET_ARM_Handle *h, int ignore_currently_down)
351{
352 uint16_t msize;
353
354 if ((GNUNET_YES == h->currently_down) && (ignore_currently_down == GNUNET_NO))
355 {
356 LOG (GNUNET_ERROR_TYPE_DEBUG,
357 "ARM connection down, not processing queue\n");
358 return;
359 }
360 if (NULL != h->cth)
361 {
362 LOG (GNUNET_ERROR_TYPE_DEBUG, "Request pending, not processing queue\n");
363 return;
364 }
365 if (NULL != h->control_pending_head)
366 msize =
367 ntohs (((struct GNUNET_MessageHeader *) &h->
368 control_pending_head[1])->size);
369 else if (GNUNET_NO == ignore_currently_down)
370 {
371 LOG (GNUNET_ERROR_TYPE_DEBUG,
372 "Request queue empty, not processing queue\n");
373 return; /* no pending message */
374 }
375 h->cth =
376 GNUNET_CLIENT_notify_transmit_ready (h->client, msize,
377 GNUNET_TIME_UNIT_FOREVER_REL,
378 GNUNET_NO, &transmit_arm_message, h);
379}
380
381
382/**
383 * Connect to arm.
192 * 384 *
385 * @param h arm handle
193 */ 386 */
194static void 387static void
195arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, 388reconnect_arm (struct GNUNET_ARM_Handle *h)
196 struct GNUNET_TIME_Relative timeout,
197 GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
198{ 389{
199 struct ShutdownContext *shutdown_ctx; 390 GNUNET_assert (NULL == h->client);
200 391 GNUNET_assert (GNUNET_YES == h->currently_down);
201 shutdown_ctx = GNUNET_malloc (sizeof (struct ShutdownContext)); 392 h->client = GNUNET_CLIENT_connect ("arm", h->cfg);
202 shutdown_ctx->cont = cont; 393 if (NULL == h->client)
203 shutdown_ctx->cont_cls = cont_cls; 394 {
204 shutdown_ctx->sock = sock; 395 LOG (GNUNET_ERROR_TYPE_DEBUG,
205 shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 396 "arm_api, GNUNET_CLIENT_connect returned NULL\n");
206 shutdown_ctx->th = GNUNET_CLIENT_notify_transmit_ready (sock, 397 if (NULL != h->conn_status)
207 sizeof (struct GNUNET_MessageHeader), 398 h->conn_status (h->conn_status_cls, h, GNUNET_NO, GNUNET_YES);
208 timeout, GNUNET_NO, &write_shutdown, 399 return;
209 shutdown_ctx); 400 }
401 LOG (GNUNET_ERROR_TYPE_DEBUG,
402 "arm_api, GNUNET_CLIENT_connect returned non-NULL\n");
403 trigger_next_request (h, GNUNET_YES);
210} 404}
211 405
212 406
213/** 407/**
214 * Setup a context for communicating with ARM. Note that this 408 * Set up a context for communicating with ARM. Note that this
215 * can be done even if the ARM service is not yet running. 409 * can be done even if the ARM service is not yet running.
410 * Never fails.
216 * 411 *
217 * @param cfg configuration to use (needed to contact ARM; 412 * @param cfg configuration to use (needed to contact ARM;
218 * the ARM service may internally use a different 413 * the ARM service may internally use a different
219 * configuration to determine how to start the service). 414 * configuration to determine how to start the service).
220 * @param service service that *this* process is implementing/providing, can be NULL 415 * @return context to use for further ARM operations
221 * @return context to use for further ARM operations, NULL on error
222 */ 416 */
223struct GNUNET_ARM_Handle * 417struct GNUNET_ARM_Handle *
224GNUNET_ARM_connect (const struct GNUNET_CONFIGURATION_Handle *cfg, 418GNUNET_ARM_alloc (const struct GNUNET_CONFIGURATION_Handle *cfg)
225 const char *service)
226{ 419{
227 struct GNUNET_ARM_Handle *ret; 420 struct GNUNET_ARM_Handle *ret;
228 421
229 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle)); 422 ret = GNUNET_malloc (sizeof (struct GNUNET_ARM_Handle));
230 ret->cfg = GNUNET_CONFIGURATION_dup (cfg); 423 ret->cfg = GNUNET_CONFIGURATION_dup (cfg);
424 ret->currently_down = GNUNET_YES;
425 ret->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
231 return ret; 426 return ret;
232} 427}
233 428
234 429
235/** 430/**
236 * Disconnect from the ARM service. 431 * Start connecting to the ARM service using the context.
237 * 432 *
238 * @param h the handle that was being used 433 * @param h ARM handle
434 * @param conn_status will be called when connecting/disconnecting
435 * @param cls closure for conn_status
239 */ 436 */
240void 437void
241GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *h) 438GNUNET_ARM_connect (struct GNUNET_ARM_Handle *h,
439 GNUNET_ARM_ConnectionStatusCallback conn_status, void *cls)
242{ 440{
243 if (h->client != NULL) 441 h->conn_status = conn_status;
244 GNUNET_CLIENT_disconnect (h->client); 442 h->conn_status_cls = cls;
245 GNUNET_CONFIGURATION_destroy (h->cfg); 443 reconnect_arm (h);
246 GNUNET_free (h);
247} 444}
248 445
249 446
250struct ARM_ShutdownContext 447/**
448 * Disconnect from the ARM service (if connected) and destroy the context.
449 * Don't call inside an ARM callback!
450 *
451 * @param h the handle that was being used
452 */
453void
454GNUNET_ARM_disconnect (struct GNUNET_ARM_Handle *handle)
251{ 455{
252 /** 456 LOG (GNUNET_ERROR_TYPE_DEBUG, "Disconnecting from ARM service\n");
253 * Callback to call once shutdown complete. 457 if (NULL != handle->cth)
254 */ 458 {
255 GNUNET_ARM_Callback cb; 459 GNUNET_CLIENT_notify_transmit_ready_cancel (handle->cth);
256 460 handle->cth = NULL;
257 /** 461 }
258 * Closure for callback. 462 clear_pending_messages (handle, GNUNET_ARM_REQUEST_DISCONNECTED);
259 */ 463 if (NULL != handle->client)
260 void *cb_cls; 464 {
261}; 465 GNUNET_CLIENT_disconnect (handle->client);
466 handle->client = NULL;
467 }
468 if (GNUNET_SCHEDULER_NO_TASK != handle->reconnect_task)
469 {
470 GNUNET_SCHEDULER_cancel (handle->reconnect_task);
471 handle->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
472 }
473 if (GNUNET_NO == handle->service_test_is_active)
474 {
475 GNUNET_CONFIGURATION_destroy (handle->cfg);
476 GNUNET_free (handle);
477 }
478}
262 479
263 480
264/** 481/**
265 * Internal state for a request with ARM. 482 * Message timed out. Remove it from the queue.
483 *
484 * @param cls the message (struct ARMControlMessage *)
485 * @param tc task context
266 */ 486 */
267struct RequestContext 487static void
488control_message_timeout (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
268{ 489{
490 struct ARMControlMessage *cm = cls;
491 struct GNUNET_ARM_Message *arm_msg;
492 LOG (GNUNET_ERROR_TYPE_DEBUG,
493 "Control message timed out\n");
494 arm_msg = cm->msg;
495 if ((NULL == arm_msg) || (0 == arm_msg->request_id))
496 {
497 GNUNET_CONTAINER_DLL_remove (cm->h->control_pending_head,
498 cm->h->control_pending_tail, cm);
499 }
500 else
501 {
502 GNUNET_CONTAINER_DLL_remove (cm->h->control_sent_head,
503 cm->h->control_sent_tail, cm);
504 }
505 if (NULL != cm->result_cont)
506 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, NULL, 0);
507 else if (NULL != cm->list_cont)
508 cm->list_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_TIMEOUT, 0, NULL);
509 GNUNET_free_non_null (cm->msg);
510 GNUNET_free (cm);
511}
269 512
270 /**
271 * Pointer to our handle with ARM.
272 */
273 struct GNUNET_ARM_Handle *h;
274
275 /**
276 * Function to call with a status code for the requested operation.
277 */
278 GNUNET_ARM_Callback callback;
279
280 /**
281 * Closure for "callback".
282 */
283 void *cls;
284
285 /**
286 * Timeout for the operation.
287 */
288 struct GNUNET_TIME_Absolute timeout;
289
290 /**
291 * Type of the request expressed as a message type (start or stop).
292 */
293 uint16_t type;
294
295 /**
296 * Flags for passing std descriptors to ARM (when starting ARM).
297 */
298 enum GNUNET_OS_InheritStdioFlags std_inheritance;
299
300};
301 513
302#include "do_start_process.c" 514#include "do_start_process.c"
303 515
@@ -308,78 +520,89 @@ struct RequestContext
308 * or not ARM is running; if it is, report success. If 520 * or not ARM is running; if it is, report success. If
309 * it is not, start the ARM process. 521 * it is not, start the ARM process.
310 * 522 *
311 * @param cls the context for the request that we will report on (struct RequestContext*) 523 * @param cls the context for the request that we will report on (struct ARMControlMessage *)
312 * @param tc why were we called (reason says if ARM is running) 524 * @param tc why were we called (reason says if ARM is running)
313 */ 525 */
314static void 526static void
315arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 527arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
316{ 528{
317 struct RequestContext *pos = cls; 529 struct ARMControlMessage *cm = cls;
318 struct GNUNET_OS_Process *proc; 530 struct GNUNET_OS_Process *proc;
531 unsigned char test_is_active;
319 char *cbinary; 532 char *cbinary;
320 char *binary; 533 char *binary;
321 char *config; 534 char *config;
322 char *loprefix; 535 char *loprefix;
323 char *lopostfix; 536 char *lopostfix;
324 537
325 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) 538 test_is_active = cm->h->service_test_is_active;
539
540 /* FIXME: shouldn't we check for GNUNET_SCHEDULER_REASON_SHUTDOWN ? */
541 if ((GNUNET_YES == test_is_active) &&
542 (0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)))
326 { 543 {
327 LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n", 544 LOG (GNUNET_ERROR_TYPE_DEBUG, "Looks like `%s' is already running.\n",
328 "gnunet-service-arm"); 545 "gnunet-service-arm");
329 /* arm is running! */ 546 /* arm is running! */
330 if (pos->callback != NULL) 547 if (cm->result_cont)
331 pos->callback (pos->cls, GNUNET_ARM_PROCESS_ALREADY_RUNNING); 548 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
332 GNUNET_free (pos); 549 }
550 if (GNUNET_NO == test_is_active)
551 {
552 /* User disconnected & destroyed ARM handle in the middle of
553 * the service test, so we kept the handle around until now.
554 */
555 GNUNET_CONFIGURATION_destroy (cm->h->cfg);
556 GNUNET_free (cm->h);
557 }
558 if ((0 != (tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE)) ||
559 (GNUNET_NO == test_is_active))
560 {
561 GNUNET_free (cm);
333 return; 562 return;
334 } 563 }
564 cm->h->service_test_is_active = GNUNET_NO;
335 LOG (GNUNET_ERROR_TYPE_DEBUG, 565 LOG (GNUNET_ERROR_TYPE_DEBUG,
336 "Looks like `%s' is not running, will start it.\n", 566 "Looks like `%s' is not running, will start it.\n",
337 "gnunet-service-arm"); 567 "gnunet-service-arm");
338 if (GNUNET_OK != 568 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
339 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "PREFIX", 569 cm->h->cfg, "arm", "PREFIX", &loprefix))
340 &loprefix))
341 loprefix = GNUNET_strdup (""); 570 loprefix = GNUNET_strdup ("");
342 if (GNUNET_OK != 571 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
343 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "OPTIONS", 572 cm->h->cfg, "arm", "OPTIONS", &lopostfix))
344 &lopostfix))
345 lopostfix = GNUNET_strdup (""); 573 lopostfix = GNUNET_strdup ("");
346 if (GNUNET_OK != 574 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (
347 GNUNET_CONFIGURATION_get_value_string (pos->h->cfg, "arm", "BINARY", 575 cm->h->cfg, "arm", "BINARY", &cbinary))
348 &cbinary))
349 { 576 {
350 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, 577 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, "arm", "BINARY");
351 "arm", "BINARY"); 578 if (cm->result_cont)
352 if (pos->callback != NULL) 579 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_NOT_KNOWN);
353 pos->callback (pos->cls, GNUNET_ARM_PROCESS_UNKNOWN); 580 GNUNET_free (cm);
354 GNUNET_free (pos);
355 GNUNET_free (loprefix); 581 GNUNET_free (loprefix);
356 GNUNET_free (lopostfix); 582 GNUNET_free (lopostfix);
357 return; 583 return;
358 } 584 }
359 if (GNUNET_OK != 585 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (
360 GNUNET_CONFIGURATION_get_value_filename (pos->h->cfg, "arm", "CONFIG", 586 cm->h->cfg, "arm", "CONFIG", &config))
361 &config))
362 config = NULL; 587 config = NULL;
363 binary = GNUNET_OS_get_libexec_binary_path (cbinary); 588 binary = GNUNET_OS_get_libexec_binary_path (cbinary);
364 GNUNET_free (cbinary); 589 GNUNET_free (cbinary);
365 if ((GNUNET_YES == 590 if ((GNUNET_YES == GNUNET_CONFIGURATION_have_value (
366 GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING", "WEAKRANDOM")) 591 cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
367 && (GNUNET_YES == 592 (GNUNET_YES == GNUNET_CONFIGURATION_get_value_yesno (
368 GNUNET_CONFIGURATION_get_value_yesno (pos->h->cfg, "TESTING", 593 cm->h->cfg, "TESTING", "WEAKRANDOM")) &&
369 "WEAKRANDOM")) 594 (GNUNET_NO == GNUNET_CONFIGURATION_have_value (
370 && (GNUNET_NO == 595 cm->h->cfg, "TESTING", "HOSTFILE")))
371 GNUNET_CONFIGURATION_have_value (pos->h->cfg, "TESTING",
372 "HOSTFILE")))
373 { 596 {
374 /* Means we are ONLY running locally */ 597 /* Means we are ONLY running locally */
375 /* we're clearly running a test, don't daemonize */ 598 /* we're clearly running a test, don't daemonize */
376 if (NULL == config) 599 if (NULL == config)
377 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 600 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
378 NULL, loprefix, binary, 601 NULL, loprefix, binary,
379 /* no daemonization! */ 602 /* no daemonization! */
380 lopostfix, NULL); 603 lopostfix, NULL);
381 else 604 else
382 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 605 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
383 NULL, loprefix, binary, "-c", config, 606 NULL, loprefix, binary, "-c", config,
384 /* no daemonization! */ 607 /* no daemonization! */
385 lopostfix, NULL); 608 lopostfix, NULL);
@@ -387,11 +610,11 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
387 else 610 else
388 { 611 {
389 if (NULL == config) 612 if (NULL == config)
390 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 613 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
391 NULL, loprefix, binary, 614 NULL, loprefix, binary,
392 "-d", lopostfix, NULL); 615 "-d", lopostfix, NULL);
393 else 616 else
394 proc = do_start_process (GNUNET_NO, pos->std_inheritance, 617 proc = do_start_process (GNUNET_NO, cm->std_inheritance,
395 NULL, loprefix, binary, "-c", config, 618 NULL, loprefix, binary, "-c", config,
396 "-d", lopostfix, NULL); 619 "-d", lopostfix, NULL);
397 } 620 }
@@ -401,52 +624,18 @@ arm_service_report (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
401 GNUNET_free (lopostfix); 624 GNUNET_free (lopostfix);
402 if (NULL == proc) 625 if (NULL == proc)
403 { 626 {
404 if (pos->callback != NULL) 627 if (cm->result_cont)
405 pos->callback (pos->cls, GNUNET_ARM_PROCESS_FAILURE); 628 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm",
406 GNUNET_free (pos); 629 GNUNET_ARM_RESULT_START_FAILED);
630 GNUNET_free (cm);
407 return; 631 return;
408 } 632 }
409 if (pos->callback != NULL) 633 if (cm->result_cont)
410 pos->callback (pos->cls, GNUNET_ARM_PROCESS_STARTING); 634 cm->result_cont (cm->cont_cls, cm->h, GNUNET_ARM_REQUEST_SENT_OK, "arm",
635 GNUNET_ARM_RESULT_STARTING);
411 GNUNET_OS_process_destroy (proc); 636 GNUNET_OS_process_destroy (proc);
412 GNUNET_free (pos); 637 reconnect_arm (cm->h);
413} 638 GNUNET_free (cm);
414
415
416/**
417 * Process a response from ARM to a request for a change in service
418 * status.
419 *
420 * @param cls the request context
421 * @param msg the response
422 */
423static void
424handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
425{
426 struct RequestContext *sc = cls;
427 const struct GNUNET_ARM_ResultMessage *res;
428 enum GNUNET_ARM_ProcessStatus status;
429
430 if ((msg == NULL) ||
431 (ntohs (msg->size) != sizeof (struct GNUNET_ARM_ResultMessage)))
432 {
433 GNUNET_break (0);
434 GNUNET_CLIENT_disconnect (sc->h->client);
435 sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg);
436 GNUNET_assert (NULL != sc->h->client);
437 if (sc->callback != NULL)
438 sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR);
439 GNUNET_free (sc);
440 return;
441 }
442 res = (const struct GNUNET_ARM_ResultMessage *) msg;
443 LOG (GNUNET_ERROR_TYPE_DEBUG,
444 "Received response from ARM for service `%s': %u\n",
445 (const char *) &sc[1], ntohs (msg->type));
446 status = (enum GNUNET_ARM_ProcessStatus) ntohl (res->status);
447 if (sc->callback != NULL)
448 sc->callback (sc->cls, status);
449 GNUNET_free (sc);
450} 639}
451 640
452 641
@@ -462,74 +651,65 @@ handle_response (void *cls, const struct GNUNET_MessageHeader *msg)
462 */ 651 */
463static void 652static void
464change_service (struct GNUNET_ARM_Handle *h, const char *service_name, 653change_service (struct GNUNET_ARM_Handle *h, const char *service_name,
465 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_Callback cb, 654 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cb,
466 void *cb_cls, uint16_t type) 655 void *cb_cls, uint16_t type)
467{ 656{
468 struct RequestContext *sctx; 657 struct ARMControlMessage *cm;
469 size_t slen; 658 size_t slen;
470 struct GNUNET_MessageHeader *msg; 659 struct GNUNET_ARM_Message *msg;
471 660
472 slen = strlen (service_name) + 1; 661 slen = strlen (service_name) + 1;
473 if (slen + sizeof (struct GNUNET_MessageHeader) >= 662 if (slen + sizeof (struct GNUNET_ARM_Message) >=
474 GNUNET_SERVER_MAX_MESSAGE_SIZE) 663 GNUNET_SERVER_MAX_MESSAGE_SIZE)
475 { 664 {
476 GNUNET_break (0); 665 GNUNET_break (0);
477 if (cb != NULL) 666 if (cb != NULL)
478 cb (cb_cls, GNUNET_NO); 667 cb (cb_cls, h, GNUNET_ARM_REQUEST_TOO_LONG, NULL, 0);
479 return; 668 return;
480 } 669 }
481 LOG (GNUNET_ERROR_TYPE_DEBUG, 670 LOG (GNUNET_ERROR_TYPE_DEBUG, "Requesting %s of service `%s'.\n",
482 (type == 671 (GNUNET_MESSAGE_TYPE_ARM_START == type) ? "start" : "termination",
483 GNUNET_MESSAGE_TYPE_ARM_START) ? 672 service_name);
484 "Requesting start of service `%s'.\n" : 673 cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
485 "Requesting termination of service `%s'.\n", service_name); 674 cm->h = h;
486 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); 675 cm->result_cont = cb;
487 sctx->h = h; 676 cm->cont_cls = cb_cls;
488 sctx->callback = cb; 677 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
489 sctx->cls = cb_cls; 678 memcpy (&cm[1], service_name, slen);
490 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 679 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message) + slen);
491 sctx->type = type; 680 msg->header.size = htons (sizeof (struct GNUNET_ARM_Message) + slen);
492 memcpy (&sctx[1], service_name, slen); 681 msg->header.type = htons (type);
493 msg = GNUNET_malloc (sizeof (struct GNUNET_MessageHeader) + slen);
494 msg->size = htons (sizeof (struct GNUNET_MessageHeader) + slen);
495 msg->type = htons (sctx->type);
496 memcpy (&msg[1], service_name, slen); 682 memcpy (&msg[1], service_name, slen);
497 if (GNUNET_OK != 683 cm->msg = msg;
498 GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, msg, 684 LOG (GNUNET_ERROR_TYPE_DEBUG,
499 GNUNET_TIME_absolute_get_remaining 685 "Inserting a control message into the queue. Timeout is %llu\n",
500 (sctx->timeout), GNUNET_YES, 686 GNUNET_TIME_absolute_get_remaining (cm->timeout).rel_value);
501 &handle_response, sctx)) 687 GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
502 { 688 h->control_pending_tail, cm);
503 GNUNET_break (0); 689 cm->timeout_task_id =
504 if (cb != NULL) 690 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
505 cb (cb_cls, GNUNET_SYSERR); 691 (cm->timeout), &control_message_timeout, cm);
506 GNUNET_free (sctx); 692 trigger_next_request (h, GNUNET_NO);
507 GNUNET_free (msg);
508 return;
509 }
510 GNUNET_free (msg);
511} 693}
512 694
513 695
514/** 696/**
515 * Start a service. 697 * Request for a service to be started.
516 * 698 *
517 * @param h handle to ARM 699 * @param h handle to ARM
518 * @param service_name name of the service 700 * @param service_name name of the service
519 * @param std_inheritance inheritance of std streams 701 * @param std_inheritance inheritance of std streams
520 * @param timeout how long to wait before failing for good 702 * @param timeout how long to wait before failing for good
521 * @param cb callback to invoke when service is ready 703 * @param cont callback to invoke after request is sent or not sent
522 * @param cb_cls closure for callback 704 * @param cont_cls closure for callback
523 */ 705 */
524void 706void
525GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h, 707GNUNET_ARM_request_service_start (struct GNUNET_ARM_Handle *h,
526 const char *service_name, 708 const char *service_name, enum GNUNET_OS_InheritStdioFlags std_inheritance,
527 enum GNUNET_OS_InheritStdioFlags std_inheritance, 709 struct GNUNET_TIME_Relative timeout, GNUNET_ARM_ResultCallback cont,
528 struct GNUNET_TIME_Relative timeout, 710 void *cont_cls)
529 GNUNET_ARM_Callback cb, void *cb_cls)
530{ 711{
531 struct RequestContext *sctx; 712 struct ARMControlMessage *cm;
532 struct GNUNET_CLIENT_Connection *client;
533 size_t slen; 713 size_t slen;
534 714
535 LOG (GNUNET_ERROR_TYPE_DEBUG, 715 LOG (GNUNET_ERROR_TYPE_DEBUG,
@@ -537,258 +717,266 @@ GNUNET_ARM_start_service (struct GNUNET_ARM_Handle *h,
537 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); 717 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
538 if (0 == strcasecmp ("arm", service_name)) 718 if (0 == strcasecmp ("arm", service_name))
539 { 719 {
540 slen = strlen ("arm") + 1; 720 /* Possible cases:
541 sctx = GNUNET_malloc (sizeof (struct RequestContext) + slen); 721 * 1) We're connected to ARM already. Invoke the callback immediately.
542 sctx->h = h; 722 * 2) We're not connected to ARM.
543 sctx->callback = cb; 723 * Cancel any reconnection attempts temporarily, then perform
544 sctx->cls = cb_cls; 724 * a service test.
545 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 725 */
546 sctx->std_inheritance = std_inheritance; 726 if (GNUNET_NO == h->currently_down)
547 memcpy (&sctx[1], service_name, slen); 727 {
548 GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report, 728 LOG (GNUNET_ERROR_TYPE_DEBUG, "ARM is already running\n");
549 sctx); 729 if (NULL != cont)
550 return; 730 cont (cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
551 } 731 }
552 if (NULL == h->client) 732 else if (GNUNET_NO == h->service_test_is_active)
553 {
554 client = GNUNET_CLIENT_connect ("arm", h->cfg);
555 if (client == NULL)
556 { 733 {
734 if (NULL != h->cth)
735 {
736 GNUNET_CLIENT_notify_transmit_ready_cancel (h->cth);
737 h->cth = NULL;
738 }
739 if (NULL != h->client)
740 {
741 GNUNET_CLIENT_disconnect (h->client);
742 h->client = NULL;
743 }
744 if (GNUNET_SCHEDULER_NO_TASK != h->reconnect_task)
745 {
746 GNUNET_SCHEDULER_cancel (h->reconnect_task);
747 h->reconnect_task = GNUNET_SCHEDULER_NO_TASK;
748 }
749
557 LOG (GNUNET_ERROR_TYPE_DEBUG, 750 LOG (GNUNET_ERROR_TYPE_DEBUG,
558 "arm_api, GNUNET_CLIENT_connect returned NULL\n"); 751 "Not connected to ARM, will do a service test\n");
559 cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR); 752
560 return; 753 slen = strlen ("arm") + 1;
754 cm = GNUNET_malloc (sizeof (struct ARMControlMessage) + slen);
755 cm->h = h;
756 cm->result_cont = cont;
757 cm->cont_cls = cont_cls;
758 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
759 cm->std_inheritance = std_inheritance;
760 memcpy (&cm[1], service_name, slen);
761 h->service_test_is_active = GNUNET_YES;
762 GNUNET_CLIENT_service_test ("arm", h->cfg, timeout, &arm_service_report,
763 cm);
561 } 764 }
562 LOG (GNUNET_ERROR_TYPE_DEBUG, 765 else
563 "arm_api, GNUNET_CLIENT_connect returned non-NULL\n"); 766 {
564 h->client = client; 767 /* Service test is already running - tell user to chill out and try
768 * again later.
769 */
770 LOG (GNUNET_ERROR_TYPE_DEBUG, "Service test is already in progress, we're busy\n");
771 if (NULL != cont)
772 cont (cont_cls, h, GNUNET_ARM_REQUEST_BUSY, NULL, 0);
773 }
774 return;
565 } 775 }
566 LOG (GNUNET_ERROR_TYPE_DEBUG, "arm_api, h->client non-NULL\n"); 776 change_service (h, service_name, timeout, cont, cont_cls,
567 change_service (h, service_name, timeout, cb, cb_cls,
568 GNUNET_MESSAGE_TYPE_ARM_START); 777 GNUNET_MESSAGE_TYPE_ARM_START);
569} 778}
570 779
571 780
572/** 781/**
573 * Callback from the arm stop service call, indicates that the arm service 782 * Request a service to be stopped.
574 * is well and truly dead, won't die, or an error occurred. 783 * Stopping arm itself will not invalidate its handle, and
575 * 784 * ARM API will try to restore connection to the ARM service,
576 * @param cls closure for the callback 785 * even if ARM connection was lost because you asked for ARM to be stopped.
577 * @param reason reason for callback 786 * Call GNUNET_ARM_disconnect () to free the handle and prevent
578 */ 787 * further connection attempts.
579static void
580arm_shutdown_callback (void *cls, enum GNUNET_ARM_ProcessStatus reason)
581{
582 struct ARM_ShutdownContext *arm_shutdown_ctx = cls;
583
584 if (arm_shutdown_ctx->cb != NULL)
585 arm_shutdown_ctx->cb (arm_shutdown_ctx->cb_cls, reason);
586 GNUNET_free (arm_shutdown_ctx);
587}
588
589
590/**
591 * Stop a service.
592 * 788 *
593 * @param h handle to ARM 789 * @param h handle to ARM
594 * @param service_name name of the service 790 * @param service_name name of the service
595 * @param timeout how long to wait before failing for good 791 * @param timeout how long to wait before failing for good
596 * @param cb callback to invoke when service is ready 792 * @param cont callback to invoke after request is sent or is not sent
597 * @param cb_cls closure for callback 793 * @param cont_cls closure for callback
598 */ 794 */
599void 795void
600GNUNET_ARM_stop_service (struct GNUNET_ARM_Handle *h, 796GNUNET_ARM_request_service_stop (struct GNUNET_ARM_Handle *h,
601 const char *service_name, 797 const char *service_name, struct GNUNET_TIME_Relative timeout,
602 struct GNUNET_TIME_Relative timeout, 798 GNUNET_ARM_ResultCallback cont, void *cont_cls)
603 GNUNET_ARM_Callback cb, void *cb_cls)
604{ 799{
605 struct ARM_ShutdownContext *arm_shutdown_ctx;
606 struct GNUNET_CLIENT_Connection *client;
607
608 LOG (GNUNET_ERROR_TYPE_DEBUG, 800 LOG (GNUNET_ERROR_TYPE_DEBUG,
609 "Stopping service `%s' within %s\n", 801 "Stopping service `%s' within %s\n",
610 service_name, 802 service_name,
611 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO)); 803 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_NO));
612 if (h->client == NULL) 804 change_service (h, service_name, timeout, cont, cont_cls,
613 {
614 client = GNUNET_CLIENT_connect ("arm", h->cfg);
615 if (client == NULL)
616 {
617 cb (cb_cls, GNUNET_SYSERR);
618 return;
619 }
620 h->client = client;
621 }
622 if (0 == strcasecmp ("arm", service_name))
623 {
624 arm_shutdown_ctx = GNUNET_malloc (sizeof (struct ARM_ShutdownContext));
625 arm_shutdown_ctx->cb = cb;
626 arm_shutdown_ctx->cb_cls = cb_cls;
627 arm_service_shutdown (h->client, timeout, &arm_shutdown_callback,
628 arm_shutdown_ctx);
629 h->client = NULL;
630 return;
631 }
632 change_service (h, service_name, timeout, cb, cb_cls,
633 GNUNET_MESSAGE_TYPE_ARM_STOP); 805 GNUNET_MESSAGE_TYPE_ARM_STOP);
634} 806}
635 807
636 808
637/** 809/**
638 * Internal state for a list request with ARM. 810 * Request a list of running services.
811 *
812 * @param h handle to ARM
813 * @param timeout how long to wait before failing for good
814 * @param cont callback to invoke after request is sent or is not sent
815 * @param cont_cls closure for callback
639 */ 816 */
640struct ListRequestContext 817void
818GNUNET_ARM_request_service_list (struct GNUNET_ARM_Handle *h,
819 struct GNUNET_TIME_Relative timeout,
820 GNUNET_ARM_ServiceListCallback cont, void *cont_cls)
641{ 821{
822 struct ARMControlMessage *cm;
823 struct GNUNET_ARM_Message *msg;
642 824
643 /** 825 LOG (GNUNET_ERROR_TYPE_DEBUG,
644 * Pointer to our handle with ARM. 826 "Requesting LIST from ARM service with timeout: %s\n",
645 */ 827 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES));
646 struct GNUNET_ARM_Handle *h;
647
648 /**
649 * Function to call with a status code for the requested operation.
650 */
651 GNUNET_ARM_List_Callback callback;
652 828
653 /** 829 cm = GNUNET_malloc (sizeof (struct ARMControlMessage));
654 * Closure for "callback". 830 cm->h = h;
655 */ 831 cm->list_cont = cont;
656 void *cls; 832 cm->cont_cls = cont_cls;
833 cm->timeout = GNUNET_TIME_relative_to_absolute (timeout);
834 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_Message));
835 msg->header.size = htons (sizeof (struct GNUNET_ARM_Message));
836 msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST);
837 cm->msg = msg;
838 GNUNET_CONTAINER_DLL_insert_tail (h->control_pending_head,
839 h->control_pending_tail, cm);
840 cm->timeout_task_id =
841 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining
842 (cm->timeout), &control_message_timeout, cm);
843 trigger_next_request (h, GNUNET_NO);
844}
657 845
658 /** 846static struct ARMControlMessage *
659 * Timeout for the operation. 847find_cm_by_id (struct GNUNET_ARM_Handle *h, uint64_t id)
660 */ 848{
661 struct GNUNET_TIME_Absolute timeout; 849 struct ARMControlMessage *result;
662}; 850 for (result = h->control_sent_head; result; result = result->next)
851 if (id == result->msg->request_id)
852 return result;
853 return NULL;
854}
663 855
664 856
665/** 857/**
666 * Process a response from ARM for the list request. 858 * Handler for ARM replies.
667 * 859 *
668 * @param cls the list request context 860 * @param cls our "struct GNUNET_ARM_Handle"
669 * @param msg the response 861 * @param msg the message received from the arm service
670 */ 862 */
671static void 863static void
672handle_list_response (void *cls, const struct GNUNET_MessageHeader *msg) 864client_notify_handler (void *cls, const struct GNUNET_MessageHeader *msg)
673{ 865{
674 struct ListRequestContext *sc = cls; 866 struct GNUNET_ARM_Handle *h = cls;
675 const struct GNUNET_ARM_ListResultMessage *res; 867
868 uint16_t msize;
869 uint64_t id;
870 unsigned char fail;
871
872 const struct GNUNET_ARM_Message *arm_msg;
873 const struct GNUNET_ARM_ResultMessage *res;
874 const struct GNUNET_ARM_ListResultMessage *lres;
875 enum GNUNET_ARM_Result result;
876 struct ARMControlMessage *cm;
877
676 const char *pos; 878 const char *pos;
677 uint16_t size_check; 879 uint16_t size_check;
678 uint16_t rcount; 880 uint16_t rcount;
679 uint16_t msize; 881
680
681 if (NULL == msg) 882 if (NULL == msg)
682 { 883 {
683 GNUNET_break (0); 884 LOG (GNUNET_ERROR_TYPE_INFO,
684 GNUNET_CLIENT_disconnect (sc->h->client); 885 _("Client was disconnected from arm service, trying to reconnect.\n"));
685 sc->h->client = GNUNET_CLIENT_connect ("arm", sc->h->cfg); 886 reconnect_arm_later (h);
686 GNUNET_assert (NULL != sc->h->client);
687 if (sc->callback != NULL)
688 sc->callback (sc->cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL);
689 GNUNET_free (sc);
690 return; 887 return;
691 } 888 }
692
693 if (NULL == sc->callback)
694 {
695 GNUNET_break (0);
696 GNUNET_free (sc);
697 return;
698 }
699 msize = ntohs (msg->size); 889 msize = ntohs (msg->size);
700 if ( (msize < sizeof ( struct GNUNET_ARM_ListResultMessage)) || 890 LOG (GNUNET_ERROR_TYPE_DEBUG,
701 (ntohs (msg->type) != GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT) ) 891 "Processing message of type %u and size %u from arm service\n",
892 ntohs (msg->type), msize);
893 if (msize < sizeof (struct GNUNET_ARM_Message))
702 { 894 {
703 GNUNET_break (0); 895 GNUNET_break (0);
704 sc->callback (sc->cls, GNUNET_NO, 0, NULL); 896 reconnect_arm_later (h);
705 GNUNET_free (sc);
706 return; 897 return;
707 } 898 }
708 size_check = 0; 899 arm_msg = (const struct GNUNET_ARM_Message *) msg;
709 res = (const struct GNUNET_ARM_ListResultMessage *) msg; 900 id = GNUNET_ntohll (arm_msg->request_id);
710 rcount = ntohs (res->count); 901 cm = find_cm_by_id (h, id);
902 if (NULL == cm)
711 { 903 {
712 const char *list[rcount]; 904 LOG (GNUNET_ERROR_TYPE_DEBUG, "Message with unknown id %llu\n", id);
713 unsigned int i; 905 return;
714
715 pos = (const char *)&res[1];
716 for (i=0; i<rcount; i++)
717 {
718 const char *end = memchr (pos, 0, msize - size_check);
719 if (NULL == end)
720 {
721 GNUNET_break (0);
722 sc->callback (sc->cls, GNUNET_NO, 0, NULL);
723 GNUNET_free (sc);
724 return;
725 }
726 list[i] = pos;
727 size_check += (end - pos) + 1;
728 pos = end + 1;
729 }
730 sc->callback (sc->cls, GNUNET_YES, rcount, list);
731 } 906 }
732 GNUNET_free (sc);
733}
734
735
736/**
737 * List all running services.
738 *
739 * @param h handle to ARM
740 * @param timeout how long to wait before failing for good
741 * @param cb callback to invoke when service is ready
742 * @param cb_cls closure for callback
743 */
744void
745GNUNET_ARM_list_running_services (struct GNUNET_ARM_Handle *h,
746 struct GNUNET_TIME_Relative timeout,
747 GNUNET_ARM_List_Callback cb, void *cb_cls)
748{
749 struct ListRequestContext *sctx;
750 struct GNUNET_MessageHeader msg;
751 struct GNUNET_CLIENT_Connection *client;
752 907
753 if (h->client == NULL) 908 fail = GNUNET_NO;
909 switch (ntohs (msg->type))
754 { 910 {
755 client = GNUNET_CLIENT_connect ("arm", h->cfg); 911 case GNUNET_MESSAGE_TYPE_ARM_RESULT:
756 if (client == NULL) 912 if (msize < sizeof (struct GNUNET_ARM_ResultMessage))
913 {
914 GNUNET_assert (0);
915 fail = GNUNET_YES;
916 break;
917 }
918 res = (const struct GNUNET_ARM_ResultMessage *) msg;
919 LOG (GNUNET_ERROR_TYPE_DEBUG,
920 "Received response from ARM for service `%s': %u\n",
921 (const char *) &cm->msg[1], ntohs (msg->type));
922 result = (enum GNUNET_ARM_Result) ntohl (res->result);
923 if (NULL != cm->result_cont)
924 cm->result_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, (const char *) &cm->msg[1], result);
925 break;
926 case GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT:
927 if (msize < sizeof (struct GNUNET_ARM_ListResultMessage))
757 { 928 {
758 GNUNET_break (0); 929 GNUNET_break (0);
759 cb (cb_cls, GNUNET_ARM_PROCESS_COMMUNICATION_ERROR, 0, NULL); 930 fail = GNUNET_YES;
760 return; 931 return;
761 } 932 }
762 h->client = client; 933 else
763 } 934 {
764 935 size_check = 0;
765 sctx = GNUNET_malloc (sizeof (struct RequestContext)); 936 lres = (const struct GNUNET_ARM_ListResultMessage *) msg;
766 sctx->h = h; 937 rcount = ntohs (lres->count);
767 sctx->callback = cb; 938 {
768 sctx->cls = cb_cls; 939 const char *list[rcount];
769 sctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 940 unsigned int i;
770 msg.size = htons (sizeof (struct GNUNET_MessageHeader)); 941
771 msg.type = htons (GNUNET_MESSAGE_TYPE_ARM_LIST); 942 pos = (const char *)&lres[1];
772 943 for (i = 0; i < rcount; i++)
773 LOG (GNUNET_ERROR_TYPE_DEBUG, 944 {
774 "Requesting LIST from ARM service with timeout: %s\n", 945 const char *end = memchr (pos, 0, msize - size_check);
775 GNUNET_STRINGS_relative_time_to_string (timeout, GNUNET_YES)); 946 if (NULL == end)
776 947 {
777 if (GNUNET_OK != 948 GNUNET_break (0);
778 GNUNET_CLIENT_transmit_and_get_response (sctx->h->client, 949 fail = GNUNET_YES;
779 &msg, 950 break;
780 GNUNET_TIME_absolute_get_remaining 951 }
781 (sctx->timeout), 952 list[i] = pos;
782 GNUNET_YES, 953 size_check += (end - pos) + 1;
783 &handle_list_response, 954 pos = end + 1;
784 sctx)) 955 }
785 { 956 if (GNUNET_YES == fail)
786 GNUNET_break (0); 957 break;
787 if (cb != NULL) 958 if (NULL != cm->list_cont)
788 cb (cb_cls, GNUNET_SYSERR, 0, NULL); 959 cm->list_cont (cm->cont_cls, h, GNUNET_ARM_REQUEST_SENT_OK, rcount, list);
789 GNUNET_free (sctx); 960 }
961 }
962 break;
963 default:
964 fail = GNUNET_YES;
790 return; 965 return;
791 } 966 }
967
968 GNUNET_assert (GNUNET_SCHEDULER_NO_TASK != cm->timeout_task_id);
969 GNUNET_SCHEDULER_cancel (cm->timeout_task_id);
970 GNUNET_CONTAINER_DLL_remove (h->control_sent_head,
971 h->control_sent_tail, cm);
972 GNUNET_free (cm->msg);
973 GNUNET_free (cm);
974
975 if (GNUNET_YES == fail)
976 reconnect_arm_later (h);
977 else
978 GNUNET_CLIENT_receive (h->client, &client_notify_handler, h,
979 GNUNET_TIME_UNIT_FOREVER_REL);
792} 980}
793 981
794/* end of arm_api.c */ 982/* end of arm_api.c */
diff --git a/src/arm/gnunet-arm.c b/src/arm/gnunet-arm.c
index 8a98ba06b..9eb64e316 100644
--- a/src/arm/gnunet-arm.c
+++ b/src/arm/gnunet-arm.c
@@ -111,6 +111,11 @@ static int ret;
111static struct GNUNET_ARM_Handle *h; 111static struct GNUNET_ARM_Handle *h;
112 112
113/** 113/**
114 * Monitor connection with ARM.
115 */
116static struct GNUNET_ARM_MonitorHandle *m;
117
118/**
114 * Our configuration. 119 * Our configuration.
115 */ 120 */
116static struct GNUNET_CONFIGURATION_Handle *cfg; 121static struct GNUNET_CONFIGURATION_Handle *cfg;
@@ -137,105 +142,6 @@ static unsigned int no_stderr;
137 142
138 143
139/** 144/**
140 * Main continuation-passing-style loop. Runs the various
141 * jobs that we've been asked to do in order.
142 *
143 * @param cls closure, unused
144 * @param tc context, unused
145 */
146static void
147cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
148
149
150/**
151 * Callback invoked with the status of the last operation. Reports to the
152 * user and then runs the next phase in the FSM.
153 *
154 * @param cls pointer to "const char*" identifying service that was manipulated
155 * @param result result of the operation
156 */
157static void
158confirm_cb (void *cls,
159 enum GNUNET_ARM_ProcessStatus result)
160{
161 const char *service = cls;
162
163 switch (result)
164 {
165 case GNUNET_ARM_PROCESS_UNKNOWN:
166 FPRINTF (stderr, _("Service `%s' is unknown to ARM.\n"), service);
167 ret = 1;
168 break;
169 case GNUNET_ARM_PROCESS_DOWN:
170 if (quiet != GNUNET_YES)
171 FPRINTF (stdout, _("Service `%s' has been stopped.\n"), service);
172 break;
173 case GNUNET_ARM_PROCESS_ALREADY_RUNNING:
174 FPRINTF (stderr, _("Service `%s' was already running.\n"), service);
175 ret = 1;
176 break;
177 case GNUNET_ARM_PROCESS_STARTING:
178 if (quiet != GNUNET_YES)
179 FPRINTF (stdout, _("Service `%s' has been started.\n"), service);
180 break;
181 case GNUNET_ARM_PROCESS_ALREADY_STOPPING:
182 FPRINTF (stderr, _("Service `%s' was already being stopped.\n"), service);
183 ret = 1;
184 break;
185 case GNUNET_ARM_PROCESS_ALREADY_DOWN:
186 FPRINTF (stderr, _("Service `%s' was already not running.\n"), service);
187 ret = 1;
188 break;
189 case GNUNET_ARM_PROCESS_SHUTDOWN:
190 FPRINTF (stderr, "%s", _("Request ignored as ARM is shutting down.\n"));
191 ret = 1;
192 break;
193 case GNUNET_ARM_PROCESS_COMMUNICATION_ERROR:
194 FPRINTF (stderr, "%s", _("Error communicating with ARM service.\n"));
195 ret = 1;
196 break;
197 case GNUNET_ARM_PROCESS_COMMUNICATION_TIMEOUT:
198 FPRINTF (stderr, "%s", _("Timeout communicating with ARM service.\n"));
199 ret = 1;
200 break;
201 case GNUNET_ARM_PROCESS_FAILURE:
202 FPRINTF (stderr, "%s", _("Operation failed.\n"));
203 ret = 1;
204 break;
205 default:
206 FPRINTF (stderr, "%s", _("Unknown response code from ARM.\n"));
207 break;
208 }
209 GNUNET_SCHEDULER_add_now (&cps_loop, NULL);
210}
211
212
213/**
214 * Callback invoked with the list of running services.
215 * Reports to the user and then runs the next phase in the FSM.
216 *
217 * @param cls currently not used
218 * @param result result of the operation
219 * @param count number of running services
220 * @param list copy of the list of running services
221 */
222static void
223list_cb (void *cls, int result, unsigned int count, const char *const*list)
224{
225 unsigned int i;
226
227 if ( (result != GNUNET_YES) || (NULL == list) )
228 {
229 FPRINTF (stderr, "%s", _("Error communicating with ARM. ARM not running?\n"));
230 return;
231 }
232 FPRINTF (stdout, "%s", _("Running services:\n"));
233 for (i=0; i<count; i++)
234 FPRINTF (stdout, "%s\n", list[i]);
235}
236
237
238/**
239 * Attempts to delete configuration file and SERVICEHOME 145 * Attempts to delete configuration file and SERVICEHOME
240 * on arm shutdown provided the end and delete options 146 * on arm shutdown provided the end and delete options
241 * were specified when gnunet-arm was run. 147 * were specified when gnunet-arm was run.
@@ -273,7 +179,9 @@ static void
273shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 179shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
274{ 180{
275 GNUNET_ARM_disconnect (h); 181 GNUNET_ARM_disconnect (h);
182 GNUNET_ARM_monitor_disconnect (m);
276 h = NULL; 183 h = NULL;
184 m = NULL;
277 if ((end == GNUNET_YES) && (delete == GNUNET_YES)) 185 if ((end == GNUNET_YES) && (delete == GNUNET_YES))
278 delete_files (); 186 delete_files ();
279 GNUNET_CONFIGURATION_destroy (cfg); 187 GNUNET_CONFIGURATION_destroy (cfg);
@@ -281,68 +189,248 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
281} 189}
282 190
283 191
192static const char *
193req_string (enum GNUNET_ARM_RequestStatus rs)
194{
195 switch (rs)
196 {
197 case GNUNET_ARM_REQUEST_SENT_OK:
198 return _("Message was sent successfully");
199 case GNUNET_ARM_REQUEST_CONFIGURATION_ERROR:
200 return _("Misconfiguration (can't connect to the ARM service)");
201 case GNUNET_ARM_REQUEST_DISCONNECTED:
202 return _("We disconnected from ARM before we could send a request");
203 case GNUNET_ARM_REQUEST_BUSY:
204 return _("ARM API is busy");
205 case GNUNET_ARM_REQUEST_TOO_LONG:
206 return _("Request doesn't fit into a message");
207 case GNUNET_ARM_REQUEST_TIMEOUT:
208 return _("Request timed out");
209 default:
210 return _("Unknown request status");
211 }
212}
213
214static const char *
215ret_string (enum GNUNET_ARM_Result result)
216{
217 switch (result)
218 {
219 case GNUNET_ARM_RESULT_STOPPED:
220 return _("%s is stopped");
221 case GNUNET_ARM_RESULT_STARTING:
222 return _("%s is starting");
223 case GNUNET_ARM_RESULT_IS_STARTING_ALREADY:
224 return _("%s is starting already");
225 case GNUNET_ARM_RESULT_IS_STOPPING_ALREADY:
226 return _("%s is stopping already");
227 case GNUNET_ARM_RESULT_IS_STARTED_ALREADY:
228 return _("%s is started already");
229 case GNUNET_ARM_RESULT_IS_STOPPED_ALREADY:
230 return _("%s is stopped already");
231 case GNUNET_ARM_RESULT_IS_NOT_KNOWN:
232 return _("%s service is not known to ARM");
233 case GNUNET_ARM_RESULT_START_FAILED:
234 return _("%s service failed to start");
235 case GNUNET_ARM_RESULT_IN_SHUTDOWN:
236 return _("%s service can't be started because ARM is shutting down");
237 default:
238 return _("Unknown result code.");
239 }
240}
241
242static void action_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
243
284/** 244/**
285 * Main function that will be run by the scheduler. 245 * Function called whenever we connect to or disconnect from ARM.
286 * 246 *
287 * @param cls closure 247 * @param cls closure
288 * @param args remaining command-line arguments 248 * @param connected GNUNET_YES if connected, GNUNET_NO if disconnected
289 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 249 * @param error GNUNET_YES if we encountered a permanent error, and there
290 * @param c configuration 250 * will be no re-connection.
291 */ 251 */
252void
253conn_status (void *cls, struct GNUNET_ARM_Handle *arm, unsigned char connected, unsigned char error)
254{
255 if (GNUNET_YES == error)
256 {
257 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
258 _("Fatal error initializing ARM API.\n"));
259 GNUNET_SCHEDULER_shutdown ();
260 return;
261 }
262/*
263 if (connected)
264 GNUNET_SCHEDULER_add_now (action_loop, NULL);
265*/
266}
267
268
292static void 269static void
293run (void *cls, char *const *args, const char *cfgfile, 270term_callback (void *cls, struct GNUNET_ARM_Handle *arm,
294 const struct GNUNET_CONFIGURATION_Handle *c) 271 enum GNUNET_ARM_RequestStatus rs, const char *service,
272 enum GNUNET_ARM_Result result)
295{ 273{
296 char *armconfig; 274 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
275 {
276 char *msg;
277 GNUNET_asprintf (&msg, _("Failed to send a request to kill the `%s' service: %%s\n"), term);
278 FPRINTF (stdout, msg, req_string (rs));
279 GNUNET_free (msg);
280 GNUNET_SCHEDULER_shutdown ();
281 }
282 if ((GNUNET_ARM_RESULT_STOPPED == result) ||
283 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result))
284 {
285 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service %s shutdown successful\n", term);
286 term = NULL;
287 GNUNET_SCHEDULER_add_now (action_loop, NULL);
288 }
289 else
290 {
291 char *msg;
292 GNUNET_asprintf (&msg, _("Failed to kill the `%s' service: %%s\n"), term);
293 FPRINTF (stdout, msg, ret_string (result));
294 GNUNET_free (msg);
295 GNUNET_SCHEDULER_shutdown ();
296 }
297}
297 298
298 cfg = GNUNET_CONFIGURATION_dup (c); 299static void
299 config_file = cfgfile; 300end_callback (void *cls, struct GNUNET_ARM_Handle *arm,
300 if (GNUNET_CONFIGURATION_get_value_string 301 enum GNUNET_ARM_RequestStatus rs, const char *service,
301 (cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK) 302 enum GNUNET_ARM_Result result)
303{
304 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
302 { 305 {
303 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 306 char *msg;
304 "PATHS", "SERVICEHOME"); 307 GNUNET_asprintf (&msg, "%s", _("Failed to send a stop request to the ARM service: %%s\n"));
305 return; 308 FPRINTF (stdout, msg, req_string (rs));
306 } 309 GNUNET_free (msg);
307 if (NULL != cfgfile) 310 GNUNET_SCHEDULER_shutdown ();
311 }
312 if ((GNUNET_ARM_RESULT_STOPPING == result) ||
313 (GNUNET_ARM_RESULT_STOPPED == result) ||
314 (GNUNET_ARM_RESULT_IS_STOPPED_ALREADY == result))
308 { 315 {
309 if (GNUNET_OK != 316 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM service shutdown successful\n");
310 GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG", 317 end = 0;
311 &armconfig)) 318 if (restart)
312 { 319 {
313 GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG", 320 restart = 0;
314 cfgfile); 321 start = 1;
322 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initiating an ARM restart\n");
315 } 323 }
316 else 324 GNUNET_SCHEDULER_add_now (action_loop, NULL);
317 GNUNET_free (armconfig);
318 } 325 }
319 if (NULL == (h = GNUNET_ARM_connect (cfg, NULL))) 326 else
320 { 327 {
321 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 328 char *msg;
322 _("Fatal error initializing ARM API.\n")); 329 GNUNET_asprintf (&msg, "%s", _("Failed to stop the ARM service: %%s\n"));
323 ret = 1; 330 FPRINTF (stdout, msg, ret_string (result));
324 GNUNET_CONFIGURATION_destroy (cfg); 331 GNUNET_free (msg);
325 cfg = NULL; 332 GNUNET_SCHEDULER_shutdown ();
333 }
334}
335
336static void
337start_callback (void *cls, struct GNUNET_ARM_Handle *arm,
338 enum GNUNET_ARM_RequestStatus rs, const char *service,
339 enum GNUNET_ARM_Result result)
340{
341 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
342 {
343 char *msg;
344 GNUNET_asprintf (&msg, "%s", _("Failed to start the ARM service: %%s\n"));
345 FPRINTF (stdout, msg, req_string (rs));
346 GNUNET_free (msg);
347 GNUNET_SCHEDULER_shutdown ();
348 }
349 if ((GNUNET_ARM_RESULT_STARTING == result) ||
350 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result))
351 {
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM service [re]start successful\n");
353 start = 0;
354 GNUNET_SCHEDULER_add_now (action_loop, NULL);
355 }
356 else
357 {
358 char *msg;
359 GNUNET_asprintf (&msg, "%s", _("Failed to start the ARM service: %%s\n"));
360 FPRINTF (stdout, msg, ret_string (result));
361 GNUNET_free (msg);
362 GNUNET_SCHEDULER_shutdown ();
363 }
364}
365
366
367static void
368init_callback (void *cls, struct GNUNET_ARM_Handle *arm,
369 enum GNUNET_ARM_RequestStatus rs, const char *service,
370 enum GNUNET_ARM_Result result)
371{
372 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
373 {
374 char *msg;
375 GNUNET_asprintf (&msg, _("Failed to send a request to start the `%s' service: %%s\n"), init);
376 FPRINTF (stdout, msg, req_string (rs));
377 GNUNET_free (msg);
378 GNUNET_SCHEDULER_shutdown ();
379 }
380 if ((GNUNET_ARM_RESULT_STARTING == result) ||
381 (GNUNET_ARM_RESULT_IS_STARTED_ALREADY == result))
382 {
383 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service %s [re]start successful\n", init);
384 init = NULL;
385 GNUNET_SCHEDULER_add_now (action_loop, NULL);
386 }
387 else
388 {
389 char *msg;
390 GNUNET_asprintf (&msg, _("Failed to start the `%s' service: %%s\n"), init);
391 FPRINTF (stdout, msg, ret_string (result));
392 GNUNET_free (msg);
393 GNUNET_SCHEDULER_shutdown ();
394 }
395}
396
397
398static void
399list_callback (void *cls, struct GNUNET_ARM_Handle *arm,
400 enum GNUNET_ARM_RequestStatus rs, unsigned int count,
401 const char *const*list)
402{
403 unsigned int i;
404 if (GNUNET_ARM_REQUEST_SENT_OK != rs)
405 {
406 char *msg;
407 GNUNET_asprintf (&msg, "%s", _("Failed to request a list of services: %%s\n"));
408 FPRINTF (stdout, msg, req_string (rs));
409 GNUNET_free (msg);
410 GNUNET_SCHEDULER_shutdown ();
411 }
412 if (NULL == list)
413 {
414 FPRINTF (stderr, "%s", _("Error communicating with ARM. ARM not running?\n"));
326 return; 415 return;
327 } 416 }
328 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, 417 FPRINTF (stdout, "%s", _("Running services:\n"));
329 &shutdown_task, NULL); 418 for (i = 0; i < count; i++)
330 GNUNET_SCHEDULER_add_now (&cps_loop, NULL); 419 FPRINTF (stdout, "%s\n", list[i]);
331} 420}
332 421
333 422
334/** 423/**
335 * Main continuation-passing-style loop. Runs the various 424 * Main action loop. Runs the various
336 * jobs that we've been asked to do in order. 425 * jobs that we've been asked to do in order.
337 * 426 *
338 * @param cls closure, unused 427 * @param cls closure, unused
339 * @param tc context, unused 428 * @param tc context, unused
340 */ 429 */
341static void 430static void
342cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) 431action_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
343{ 432{
344 if (NULL == h) 433 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Running requested actions\n");
345 return;
346 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) 434 if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
347 return; 435 return;
348 while (1) 436 while (1)
@@ -352,82 +440,54 @@ cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
352 case 0: 440 case 0:
353 if (NULL != term) 441 if (NULL != term)
354 { 442 {
355 GNUNET_ARM_stop_service (h, term, 443 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Termination action\n");
356 (0 == 444 GNUNET_ARM_request_service_stop (h, term, (0 ==
357 timeout.rel_value) ? STOP_TIMEOUT : 445 timeout.rel_value) ? STOP_TIMEOUT : timeout,
358 timeout, &confirm_cb, term); 446 term_callback, NULL);
359 return; 447 return;
360 } 448 }
361 break; 449 break;
362 case 1: 450 case 1:
363 if ((end) || (restart)) 451 if (end || restart)
364 { 452 {
365 GNUNET_ARM_stop_service (h, "arm", 453 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "End action\n");
366 (0 == 454 GNUNET_ARM_request_service_stop (h, "arm", (0 ==
367 timeout.rel_value) ? STOP_TIMEOUT_ARM 455 timeout.rel_value) ? STOP_TIMEOUT_ARM : timeout,
368 : timeout, &confirm_cb, "arm"); 456 end_callback, NULL);
369 return; 457 return;
370 } 458 }
371 break; 459 break;
372 case 2: 460 case 2:
373 if (start) 461 if (start)
374 { 462 {
375 GNUNET_ARM_start_service (h, "arm", 463 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Start action\n");
376 (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) | 464 GNUNET_ARM_request_service_start (h, "arm",
377 (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR), 465 (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
378 (0 == 466 (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
379 timeout.rel_value) ? START_TIMEOUT : 467 (0 == timeout.rel_value) ? START_TIMEOUT: timeout,
380 timeout, &confirm_cb, "arm"); 468 start_callback, NULL);
381 return; 469 return;
382 } 470 }
383 break; 471 break;
384 case 3: 472 case 3:
385 if (NULL != init) 473 if (NULL != init)
386 {
387 GNUNET_ARM_start_service (h, init,
388 (no_stdout ? 0 : GNUNET_OS_INHERIT_STD_OUT) |
389 (no_stderr ? 0 : GNUNET_OS_INHERIT_STD_ERR),
390 (0 ==
391 timeout.rel_value) ? START_TIMEOUT :
392 timeout, &confirm_cb, init);
393 return;
394 }
395 break;
396 case 4:
397 if (restart)
398 { 474 {
399 GNUNET_ARM_disconnect (h); 475 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Initialization action\n");
400 phase = 0; 476 GNUNET_ARM_request_service_start (h, init, GNUNET_OS_INHERIT_STD_NONE,
401 end = 0; 477 (0 == timeout.rel_value) ? STOP_TIMEOUT : timeout,
402 start = 1; 478 init_callback, NULL);
403 restart = 0; 479 return;
404 if (NULL == (h = GNUNET_ARM_connect (cfg, NULL)))
405 {
406 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
407 _("Fatal error initializing ARM API.\n"));
408 ret = 1;
409 return;
410 }
411 GNUNET_SCHEDULER_add_now (&cps_loop, NULL);
412 return;
413 } 480 }
414 break; 481 break;
415 case 5: 482 case 4:
416 if (list) 483 if (list)
417 { 484 {
418 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 485 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
419 "Going to list all running services controlled by ARM.\n"); 486 "Going to list all running services controlled by ARM.\n");
420 487
421 if (NULL == h) 488 GNUNET_ARM_request_service_list (h,
422 { 489 (0 == timeout.rel_value) ? LIST_TIMEOUT : timeout,
423 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 490 list_callback, &list);
424 _("Fatal error initializing ARM API.\n"));
425 return;
426 }
427 GNUNET_ARM_list_running_services (h,
428 (0 ==
429 timeout.rel_value) ? LIST_TIMEOUT :
430 timeout, &list_cb, NULL);
431 return; 491 return;
432 } 492 }
433 /* Fall through */ 493 /* Fall through */
@@ -440,6 +500,89 @@ cps_loop (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
440 500
441 501
442/** 502/**
503 * Function called when a service starts or stops.
504 *
505 * @param cls closure
506 * @param service service name
507 * @param status status of the service
508 */
509static void
510srv_status (void *cls, struct GNUNET_ARM_MonitorHandle *arm,
511 const char *service, enum GNUNET_ARM_ServiceStatus status)
512{
513 const char *msg;
514 switch (status)
515 {
516 case GNUNET_ARM_SERVICE_MONITORING_STARTED:
517 msg = _("Began monitoring ARM for service status changes\n");
518 break;
519 case GNUNET_ARM_SERVICE_STOPPED:
520 msg = _("Stopped %s.\n");
521 break;
522 case GNUNET_ARM_SERVICE_STARTING:
523 msg = _("Starting %s...\n");
524 break;
525 case GNUNET_ARM_SERVICE_STOPPING:
526 msg = _("Stopping %s...\n");
527 break;
528 default:
529 msg = NULL;
530 break;
531 }
532 if (NULL != msg)
533 FPRINTF (stderr, msg, service);
534 else
535 FPRINTF (stderr, _("Unknown status %u for service %s.\n"), status, service);
536 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Got service %s status %u\n", service, status);
537}
538
539
540/**
541 * Main function that will be run by the scheduler.
542 *
543 * @param cls closure
544 * @param args remaining command-line arguments
545 * @param cfgfile name of the configuration file used (for saving, can be NULL!)
546 * @param c configuration
547 */
548static void
549run (void *cls, char *const *args, const char *cfgfile,
550 const struct GNUNET_CONFIGURATION_Handle *c)
551{
552 char *armconfig;
553
554 cfg = GNUNET_CONFIGURATION_dup (c);
555 config_file = cfgfile;
556 if (GNUNET_CONFIGURATION_get_value_string
557 (cfg, "PATHS", "SERVICEHOME", &dir) != GNUNET_OK)
558 {
559 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
560 "PATHS", "SERVICEHOME");
561 return;
562 }
563 if (NULL != cfgfile)
564 {
565 if (GNUNET_OK !=
566 GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG",
567 &armconfig))
568 {
569 GNUNET_CONFIGURATION_set_value_string (cfg, "arm", "CONFIG",
570 cfgfile);
571 }
572 else
573 GNUNET_free (armconfig);
574 }
575 h = GNUNET_ARM_alloc (cfg);
576 m = GNUNET_ARM_monitor_alloc (cfg);
577 GNUNET_ARM_connect (h, conn_status, NULL);
578 GNUNET_ARM_monitor (m, srv_status, NULL);
579 GNUNET_SCHEDULER_add_now (action_loop, NULL);
580 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
581 shutdown_task, NULL);
582}
583
584
585/**
443 * The main function to obtain arm from gnunetd. 586 * The main function to obtain arm from gnunetd.
444 * 587 *
445 * @param argc number of arguments from the command line 588 * @param argc number of arguments from the command line
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c
index 7a99b5079..b1ba5af1b 100644
--- a/src/arm/gnunet-service-arm.c
+++ b/src/arm/gnunet-service-arm.c
@@ -30,6 +30,13 @@
30#include "arm.h" 30#include "arm.h"
31 31
32/** 32/**
33 * How many messages do we queue up at most for optional
34 * notifications to a client? (this can cause notifications
35 * about outgoing messages to be dropped).
36 */
37#define MAX_NOTIFY_QUEUE 1024
38
39/**
33 * List of our services. 40 * List of our services.
34 */ 41 */
35struct ServiceList; 42struct ServiceList;
@@ -125,6 +132,11 @@ struct ServiceList
125 struct GNUNET_SERVER_Client *killing_client; 132 struct GNUNET_SERVER_Client *killing_client;
126 133
127 /** 134 /**
135 * ID of the request that killed the service (for reporting back).
136 */
137 uint64_t killing_client_request_id;
138
139 /**
128 * Process structure pointer of the child. 140 * Process structure pointer of the child.
129 */ 141 */
130 struct GNUNET_OS_Process *proc; 142 struct GNUNET_OS_Process *proc;
@@ -217,17 +229,154 @@ static int in_shutdown;
217 */ 229 */
218static struct GNUNET_SERVER_Handle *server; 230static struct GNUNET_SERVER_Handle *server;
219 231
232/**
233 * Context for notifications we need to send to our clients.
234 */
235static struct GNUNET_SERVER_NotificationContext *notifier;
236
220 237
221#include "do_start_process.c" 238#include "do_start_process.c"
222 239
240/**
241 * Transmit a status result message.
242 *
243 * @param cls pointer to "unit16_t*" with message type
244 * @param size number of bytes available in buf
245 * @param buf where to copy the message, NULL on error
246 * @return number of bytes copied to buf
247 */
248static size_t
249write_result (void *cls, size_t size, void *buf)
250{
251 struct GNUNET_ARM_ResultMessage *msg = cls;
252 size_t msize;
253
254 if (buf == NULL)
255 {
256 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
257 _("Could not send status result to client\n"));
258 GNUNET_free (msg);
259 return 0; /* error, not much we can do */
260 }
261 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
262 "Sending status response %u to client\n", (unsigned int) msg->result);
263 msize = msg->arm_msg.header.size;
264 GNUNET_assert (size >= msize);
265 msg->arm_msg.header.size = htons (msg->arm_msg.header.size);
266 msg->arm_msg.header.type = htons (msg->arm_msg.header.type);
267 msg->result = htonl (msg->result);
268 msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id);
269 memcpy (buf, msg, msize);
270 GNUNET_free (msg);
271 return msize;
272}
273
274/**
275 * Transmit the list of running services.
276 *
277 * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message
278 * @param size number of bytes available in buf
279 * @param buf where to copy the message, NULL on error
280 * @return number of bytes copied to buf
281 */
282static size_t
283write_list_result (void *cls, size_t size, void *buf)
284{
285 struct GNUNET_ARM_ListResultMessage *msg = cls;
286 size_t rslt_size;
287
288 if (buf == NULL)
289 {
290 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
291 _("Could not send list result to client\n"));
292 GNUNET_free (msg);
293 return 0; /* error, not much we can do */
294 }
295
296 rslt_size = msg->arm_msg.header.size;
297 GNUNET_assert (size >= rslt_size);
298 msg->arm_msg.header.size = htons (msg->arm_msg.header.size);
299 msg->arm_msg.header.type = htons (msg->arm_msg.header.type);
300 msg->arm_msg.request_id = GNUNET_htonll (msg->arm_msg.request_id);
301 msg->count = htons (msg->count);
302
303 memcpy (buf, msg, rslt_size);
304 GNUNET_free (msg);
305 return rslt_size;
306}
307
308
309/**
310 * Signal our client that we will start or stop the
311 * service.
312 *
313 * @param client who is being signalled
314 * @param name name of the service
315 * @param result message type to send
316 * @return NULL if it was not found
317 */
318static void
319signal_result (struct GNUNET_SERVER_Client *client, const char *name,
320 uint64_t request_id, enum GNUNET_ARM_Result result)
321{
322 struct GNUNET_ARM_ResultMessage *msg;
323 size_t msize;
324
325 msize = sizeof (struct GNUNET_ARM_ResultMessage);
326 msg = GNUNET_malloc (msize);
327 msg->arm_msg.header.size = msize;
328 msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_RESULT;
329 msg->result = result;
330 msg->arm_msg.request_id = request_id;
331
332 GNUNET_SERVER_notify_transmit_ready (client, msize,
333 GNUNET_TIME_UNIT_FOREVER_REL, write_result, msg);
334}
335
336
337/**
338 * Tell all clients about status change of a service.
339 *
340 * @param name name of the service
341 * @param status message type to send
342 */
343static void
344broadcast_status (const char *name, enum GNUNET_ARM_ServiceStatus status,
345 struct GNUNET_SERVER_Client *unicast)
346{
347 struct GNUNET_ARM_StatusMessage *msg;
348 size_t namelen;
349
350 if (NULL == notifier)
351 return;
352 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
353 "Sending status %u of service `%s' to client\n",
354 (unsigned int) status, name);
355 namelen = strlen (name);
356 msg = GNUNET_malloc (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1);
357 msg->header.size = htons (sizeof (struct GNUNET_ARM_StatusMessage) + namelen + 1);
358 msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_STATUS);
359 msg->status = htonl ((uint32_t) (status));
360 memcpy ((char *) &msg[1], name, namelen + 1);
361
362 if (NULL == unicast)
363 GNUNET_SERVER_notification_context_broadcast (notifier,
364 (struct GNUNET_MessageHeader *) msg, GNUNET_YES);
365 else
366 GNUNET_SERVER_notification_context_unicast (notifier, unicast,
367 (const struct GNUNET_MessageHeader *) msg, GNUNET_NO);
368 GNUNET_free (msg);
369}
370
223 371
224/** 372/**
225 * Actually start the process for the given service. 373 * Actually start the process for the given service.
226 * 374 *
227 * @param sl identifies service to start 375 * @param sl identifies service to start
376 * @param client that asked to start the service (may be NULL)
228 */ 377 */
229static void 378static void
230start_process (struct ServiceList *sl) 379start_process (struct ServiceList *sl, struct GNUNET_SERVER_Client *client, uint64_t request_id)
231{ 380{
232 char *loprefix; 381 char *loprefix;
233 char *options; 382 char *options;
@@ -342,11 +491,20 @@ start_process (struct ServiceList *sl)
342 } 491 }
343 GNUNET_free (binary); 492 GNUNET_free (binary);
344 if (sl->proc == NULL) 493 if (sl->proc == NULL)
494 {
345 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"), 495 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"),
346 sl->name); 496 sl->name);
497 if (client)
498 signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_START_FAILED);
499 }
347 else 500 else
501 {
348 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), 502 GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"),
349 sl->name); 503 sl->name);
504 broadcast_status (sl->name, GNUNET_ARM_SERVICE_STARTING, NULL);
505 if (client)
506 signal_result (client, sl->name, request_id, GNUNET_ARM_RESULT_STARTING);
507 }
350 /* clean up */ 508 /* clean up */
351 GNUNET_free (loprefix); 509 GNUNET_free (loprefix);
352 GNUNET_free (options); 510 GNUNET_free (options);
@@ -355,104 +513,6 @@ start_process (struct ServiceList *sl)
355 513
356 514
357/** 515/**
358 * Transmit a status result message.
359 *
360 * @param cls pointer to "unit16_t*" with message type
361 * @param size number of bytes available in buf
362 * @param buf where to copy the message, NULL on error
363 * @return number of bytes copied to buf
364 */
365static size_t
366write_result (void *cls, size_t size, void *buf)
367{
368 enum GNUNET_ARM_ProcessStatus *res = cls;
369 struct GNUNET_ARM_ResultMessage *msg;
370
371 if (buf == NULL)
372 {
373 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
374 _("Could not send status result to client\n"));
375 return 0; /* error, not much we can do */
376 }
377 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
378 "Sending status response %u to client\n", (unsigned int) *res);
379 GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage));
380 msg = buf;
381 msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage));
382 msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT);
383 msg->status = htonl ((uint32_t) (*res));
384 GNUNET_free (res);
385 return sizeof (struct GNUNET_ARM_ResultMessage);
386}
387
388/**
389 * Transmit the list of running services.
390 *
391 * @param cls pointer to struct GNUNET_ARM_ListResultMessage with the message
392 * @param size number of bytes available in buf
393 * @param buf where to copy the message, NULL on error
394 * @return number of bytes copied to buf
395 */
396static size_t
397write_list_result (void *cls, size_t size, void *buf)
398{
399 struct GNUNET_ARM_ListResultMessage *msg = cls;
400 struct GNUNET_ARM_ListResultMessage *rslt;
401 size_t rslt_size;
402
403 if (buf == NULL)
404 {
405 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
406 _("Could not send list result to client\n"));
407 return 0; /* error, not much we can do */
408 }
409
410 GNUNET_assert (size >= msg->header.size);
411 rslt = buf;
412 rslt->header.size = htons (msg->header.size);
413 rslt->header.type = htons (msg->header.type);
414 rslt->count = htons (msg->count);
415
416 size_t list_size = msg->header.size
417 - sizeof (struct GNUNET_ARM_ListResultMessage);
418 memcpy (&rslt[1], &msg[1], list_size);
419
420 rslt_size = msg->header.size;
421 GNUNET_free (msg);
422 return rslt_size;
423}
424
425
426/**
427 * Signal our client that we will start or stop the
428 * service.
429 *
430 * @param client who is being signalled
431 * @param name name of the service
432 * @param result message type to send
433 * @return NULL if it was not found
434 */
435static void
436signal_result (struct GNUNET_SERVER_Client *client, const char *name,
437 enum GNUNET_ARM_ProcessStatus result)
438{
439 enum GNUNET_ARM_ProcessStatus *res;
440
441 if (NULL == client)
442 return;
443 /* FIXME: this is not super-clean yet... */
444 res = GNUNET_malloc (sizeof (enum GNUNET_ARM_ProcessStatus));
445 *res = result;
446 GNUNET_SERVER_notify_transmit_ready (client,
447 sizeof (struct
448 GNUNET_ARM_ResultMessage),
449 GNUNET_TIME_UNIT_FOREVER_REL,
450 &write_result, res);
451 GNUNET_SERVER_receive_done (client, GNUNET_OK);
452}
453
454
455/**
456 * Find the process with the given service 516 * Find the process with the given service
457 * name in the given list and return it. 517 * name in the given list and return it.
458 * 518 *
@@ -492,7 +552,7 @@ accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
492 GNUNET_assert (GNUNET_NO == in_shutdown); 552 GNUNET_assert (GNUNET_NO == in_shutdown);
493 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) 553 if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason))
494 return; 554 return;
495 start_process (sl); 555 start_process (sl, NULL, 0);
496} 556}
497 557
498 558
@@ -619,10 +679,14 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client,
619 const char *servicename; 679 const char *servicename;
620 struct ServiceList *sl; 680 struct ServiceList *sl;
621 uint16_t size; 681 uint16_t size;
622 682 uint64_t request_id;
623 size = ntohs (message->size); 683 struct GNUNET_ARM_Message *amsg;
624 size -= sizeof (struct GNUNET_MessageHeader); 684
625 servicename = (const char *) &message[1]; 685 amsg = (struct GNUNET_ARM_Message *) message;
686 request_id = GNUNET_ntohll (amsg->request_id);
687 size = ntohs (amsg->header.size);
688 size -= sizeof (struct GNUNET_ARM_Message);
689 servicename = (const char *) &amsg[1];
626 if ((size == 0) || (servicename[size - 1] != '\0')) 690 if ((size == 0) || (servicename[size - 1] != '\0'))
627 { 691 {
628 GNUNET_break (0); 692 GNUNET_break (0);
@@ -631,23 +695,40 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client,
631 } 695 }
632 if (GNUNET_YES == in_shutdown) 696 if (GNUNET_YES == in_shutdown)
633 { 697 {
634 signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); 698 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN);
699 GNUNET_SERVER_receive_done (client, GNUNET_OK);
635 return; 700 return;
636 } 701 }
637 sl = find_service (servicename); 702 sl = find_service (servicename);
638 if (NULL == sl) 703 if (NULL == sl)
639 { 704 {
640 signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); 705 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN);
706 GNUNET_SERVER_receive_done (client, GNUNET_OK);
641 return; 707 return;
642 } 708 }
643 sl->is_default = GNUNET_YES; 709 sl->is_default = GNUNET_YES;
644 if (sl->proc != NULL) 710 if (sl->proc != NULL)
645 { 711 {
646 signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_RUNNING); 712 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_STARTED_ALREADY);
713 GNUNET_SERVER_receive_done (client, GNUNET_OK);
647 return; 714 return;
648 } 715 }
649 start_process (sl); 716 start_process (sl, client, request_id);
650 signal_result (client, servicename, GNUNET_ARM_PROCESS_STARTING); 717 GNUNET_SERVER_receive_done (client, GNUNET_OK);
718}
719
720
721/**
722 * Start a shutdown sequence.
723 *
724 * @param cls closure (refers to service)
725 * @param tc task context
726 */
727static void
728trigger_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
729{
730 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Triggering shutdown\n");
731 GNUNET_SCHEDULER_shutdown ();
651} 732}
652 733
653 734
@@ -667,10 +748,14 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
667 struct ServiceList *sl; 748 struct ServiceList *sl;
668 const char *servicename; 749 const char *servicename;
669 uint16_t size; 750 uint16_t size;
670 751 uint64_t request_id;
671 size = ntohs (message->size); 752 struct GNUNET_ARM_Message *amsg;
672 size -= sizeof (struct GNUNET_MessageHeader); 753
673 servicename = (const char *) &message[1]; 754 amsg = (struct GNUNET_ARM_Message *) message;
755 request_id = GNUNET_ntohll (amsg->request_id);
756 size = ntohs (amsg->header.size);
757 size -= sizeof (struct GNUNET_ARM_Message);
758 servicename = (const char *) &amsg[1];
674 if ((size == 0) || (servicename[size - 1] != '\0')) 759 if ((size == 0) || (servicename[size - 1] != '\0'))
675 { 760 {
676 GNUNET_break (0); 761 GNUNET_break (0);
@@ -679,40 +764,58 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
679 } 764 }
680 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 765 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
681 _("Preparing to stop `%s'\n"), servicename); 766 _("Preparing to stop `%s'\n"), servicename);
767 if (0 == strcasecmp (servicename, "arm"))
768 {
769 broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
770 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_STOPPING);
771 GNUNET_SERVER_client_persist_ (client);
772 GNUNET_SCHEDULER_add_now (trigger_shutdown, NULL);
773 GNUNET_SERVER_receive_done (client, GNUNET_OK);
774 return;
775 }
682 sl = find_service (servicename); 776 sl = find_service (servicename);
683 if (sl == NULL) 777 if (sl == NULL)
684 { 778 {
685 signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); 779 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IS_NOT_KNOWN);
780 GNUNET_SERVER_receive_done (client, GNUNET_OK);
686 return; 781 return;
687 } 782 }
688 sl->is_default = GNUNET_NO; 783 sl->is_default = GNUNET_NO;
689 if (GNUNET_YES == in_shutdown) 784 if (GNUNET_YES == in_shutdown)
690 { 785 {
691 /* shutdown in progress */ 786 /* shutdown in progress */
692 signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); 787 signal_result (client, servicename, request_id, GNUNET_ARM_RESULT_IN_SHUTDOWN);
788 GNUNET_SERVER_receive_done (client, GNUNET_OK);
693 return; 789 return;
694 } 790 }
695 if (sl->killing_client != NULL) 791 if (sl->killing_client != NULL)
696 { 792 {
697 /* killing already in progress */ 793 /* killing already in progress */
698 signal_result (client, servicename, 794 signal_result (client, servicename, request_id,
699 GNUNET_ARM_PROCESS_ALREADY_STOPPING); 795 GNUNET_ARM_RESULT_IS_STOPPING_ALREADY);
796 GNUNET_SERVER_receive_done (client, GNUNET_OK);
700 return; 797 return;
701 } 798 }
702 if (sl->proc == NULL) 799 if (sl->proc == NULL)
703 { 800 {
704 /* process is down */ 801 /* process is down */
705 signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN); 802 signal_result (client, servicename, request_id,
803 GNUNET_ARM_RESULT_IS_STOPPED_ALREADY);
804 GNUNET_SERVER_receive_done (client, GNUNET_OK);
706 return; 805 return;
707 } 806 }
708 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 807 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
709 "Sending kill signal to service `%s', waiting for process to die.\n", 808 "Sending kill signal to service `%s', waiting for process to die.\n",
710 servicename); 809 servicename);
810 broadcast_status (servicename, GNUNET_ARM_SERVICE_STOPPING, NULL);
811 /* no signal_start - only when it's STOPPED */
711 sl->killed_at = GNUNET_TIME_absolute_get (); 812 sl->killed_at = GNUNET_TIME_absolute_get ();
712 if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM)) 813 if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM))
713 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); 814 GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill");
714 sl->killing_client = client; 815 sl->killing_client = client;
816 sl->killing_client_request_id = request_id;
715 GNUNET_SERVER_client_keep (client); 817 GNUNET_SERVER_client_keep (client);
818 GNUNET_SERVER_receive_done (client, GNUNET_YES);
716} 819}
717 820
718/** 821/**
@@ -727,6 +830,7 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
727 const struct GNUNET_MessageHeader *message) 830 const struct GNUNET_MessageHeader *message)
728{ 831{
729 struct GNUNET_ARM_ListResultMessage *msg; 832 struct GNUNET_ARM_ListResultMessage *msg;
833 struct GNUNET_ARM_Message *request;
730 size_t string_list_size; 834 size_t string_list_size;
731 size_t total_size; 835 size_t total_size;
732 struct ServiceList *sl; 836 struct ServiceList *sl;
@@ -735,6 +839,7 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
735 if (NULL == client) 839 if (NULL == client)
736 return; 840 return;
737 841
842 request = (struct GNUNET_ARM_Message *) message;
738 count = 0; 843 count = 0;
739 string_list_size = 0; 844 string_list_size = 0;
740 /* first count the running processes get their name's size */ 845 /* first count the running processes get their name's size */
@@ -748,11 +853,13 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
748 count++; 853 count++;
749 } 854 }
750 } 855 }
856
751 total_size = sizeof (struct GNUNET_ARM_ListResultMessage) 857 total_size = sizeof (struct GNUNET_ARM_ListResultMessage)
752 + string_list_size; 858 + string_list_size;
753 msg = GNUNET_malloc (total_size); 859 msg = GNUNET_malloc (total_size);
754 msg->header.size = total_size; 860 msg->arm_msg.header.size = total_size;
755 msg->header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT; 861 msg->arm_msg.header.type = GNUNET_MESSAGE_TYPE_ARM_LIST_RESULT;
862 msg->arm_msg.request_id = GNUNET_ntohll (request->request_id);
756 msg->count = count; 863 msg->count = count;
757 864
758 char *pos = (char *)&msg[1]; 865 char *pos = (char *)&msg[1];
@@ -767,9 +874,9 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
767 } 874 }
768 875
769 GNUNET_SERVER_notify_transmit_ready (client, 876 GNUNET_SERVER_notify_transmit_ready (client,
770 msg->header.size, 877 total_size,
771 GNUNET_TIME_UNIT_FOREVER_REL, 878 GNUNET_TIME_UNIT_FOREVER_REL,
772 &write_list_result, msg); 879 write_list_result, msg);
773 GNUNET_SERVER_receive_done (client, GNUNET_OK); 880 GNUNET_SERVER_receive_done (client, GNUNET_OK);
774} 881}
775 882
@@ -780,6 +887,12 @@ handle_list (void *cls, struct GNUNET_SERVER_Client *client,
780static void 887static void
781do_shutdown () 888do_shutdown ()
782{ 889{
890 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Last shutdown phase\n");
891 if (NULL != notifier)
892 {
893 GNUNET_SERVER_notification_context_destroy (notifier);
894 notifier = NULL;
895 }
783 if (NULL != server) 896 if (NULL != server)
784 { 897 {
785 GNUNET_SERVER_destroy (server); 898 GNUNET_SERVER_destroy (server);
@@ -792,6 +905,15 @@ do_shutdown ()
792 } 905 }
793} 906}
794 907
908unsigned int
909list_count (struct ServiceList *running_head)
910{
911 struct ServiceList *i;
912 unsigned int res = 0;
913 for (res = 0, i = running_head; i; i = i->next, res++)
914 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "%s\n", i->name);
915 return res;
916}
795 917
796/** 918/**
797 * Task run for shutdown. 919 * Task run for shutdown.
@@ -806,6 +928,7 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
806 struct ServiceList *nxt; 928 struct ServiceList *nxt;
807 struct ServiceListeningInfo *sli; 929 struct ServiceListeningInfo *sli;
808 930
931 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "First shutdown phase\n");
809 if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) 932 if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
810 { 933 {
811 GNUNET_SCHEDULER_cancel (child_restart_task); 934 GNUNET_SCHEDULER_cancel (child_restart_task);
@@ -851,6 +974,9 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
851 /* finally, should all service processes be already gone, terminate for real */ 974 /* finally, should all service processes be already gone, terminate for real */
852 if (running_head == NULL) 975 if (running_head == NULL)
853 do_shutdown (); 976 do_shutdown ();
977 else
978 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
979 "Delaying shutdown, have %u childs still running\n", list_count (running_head));
854} 980}
855 981
856 982
@@ -890,7 +1016,7 @@ delayed_restart_task (void *cls,
890 /* process should run by default, start immediately */ 1016 /* process should run by default, start immediately */
891 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1017 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
892 _("Restarting service `%s'.\n"), sl->name); 1018 _("Restarting service `%s'.\n"), sl->name);
893 start_process (sl); 1019 start_process (sl, NULL, 0);
894 } 1020 }
895 else 1021 else
896 { 1022 {
@@ -1004,74 +1130,63 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
1004 } 1130 }
1005 GNUNET_OS_process_destroy (pos->proc); 1131 GNUNET_OS_process_destroy (pos->proc);
1006 pos->proc = NULL; 1132 pos->proc = NULL;
1133 broadcast_status (pos->name, GNUNET_ARM_SERVICE_STOPPED, NULL);
1007 if (NULL != pos->killing_client) 1134 if (NULL != pos->killing_client)
1008 { 1135 {
1009 signal_result (pos->killing_client, pos->name, 1136 signal_result (pos->killing_client, pos->name,
1010 GNUNET_ARM_PROCESS_DOWN); 1137 pos->killing_client_request_id, GNUNET_ARM_RESULT_STOPPED);
1011 GNUNET_SERVER_client_drop (pos->killing_client); 1138 GNUNET_SERVER_client_drop (pos->killing_client);
1012 pos->killing_client = NULL; 1139 pos->killing_client = NULL;
1013 /* process can still be re-started on-demand, ensure it is re-started if there is demand */ 1140 pos->killing_client_request_id = 0;
1014 for (sli = pos->listen_head; NULL != sli; sli = sli->next) 1141 /* process can still be re-started on-demand, ensure it is re-started if there is demand */
1015 { 1142 for (sli = pos->listen_head; NULL != sli; sli = sli->next)
1016 GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task); 1143 {
1017 sli->accept_task = 1144 GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task);
1018 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, 1145 sli->accept_task =
1019 sli->listen_socket, 1146 GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL,
1020 &accept_connection, sli); 1147 sli->listen_socket, &accept_connection, sli);
1021 } 1148 }
1022 } 1149 }
1023 if (GNUNET_YES != in_shutdown) 1150 if (GNUNET_YES != in_shutdown)
1024 { 1151 {
1025 if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0)) 1152 if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0))
1026 { 1153 {
1027 /* process terminated normally, allow restart at any time */ 1154 /* process terminated normally, allow restart at any time */
1028 pos->restart_at.abs_value = 0; 1155 pos->restart_at.abs_value = 0;
1029 } 1156 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1030 else 1157 _("Service `%s' terminated normally, will restart at any time\n"),
1031 { 1158 pos->name);
1032 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1033 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1034 _
1035 ("Service `%s' terminated with status %s/%d, will restart in %s\n"),
1036 pos->name, statstr, statcode,
1037 GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES));
1038 /* schedule restart */
1039 pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
1040 pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
1041 }
1042 if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
1043 GNUNET_SCHEDULER_cancel (child_restart_task);
1044 child_restart_task =
1045 GNUNET_SCHEDULER_add_with_priority
1046 (GNUNET_SCHEDULER_PRIORITY_IDLE,
1047 &delayed_restart_task, NULL);
1048 } 1159 }
1160 else
1161 {
1162 if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN))
1163 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1164 _("Service `%s' terminated with status %s/%d, will restart in %s\n"),
1165 pos->name, statstr, statcode,
1166 GNUNET_STRINGS_relative_time_to_string (pos->backoff, GNUNET_YES));
1167 /* schedule restart */
1168 pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff);
1169 pos->backoff = GNUNET_TIME_STD_BACKOFF (pos->backoff);
1170 }
1171 if (GNUNET_SCHEDULER_NO_TASK != child_restart_task)
1172 GNUNET_SCHEDULER_cancel (child_restart_task);
1173 child_restart_task = GNUNET_SCHEDULER_add_with_priority (
1174 GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL);
1175 }
1049 else 1176 else
1050 { 1177 {
1051 free_service (pos); 1178 free_service (pos);
1052 } 1179 }
1053 } 1180 }
1054 child_death_task = 1181 child_death_task = GNUNET_SCHEDULER_add_read_file (
1055 GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, 1182 GNUNET_TIME_UNIT_FOREVER_REL, pr, &maint_child_death, NULL);
1056 pr, &maint_child_death, NULL);
1057 if ((NULL == running_head) && (GNUNET_YES == in_shutdown)) 1183 if ((NULL == running_head) && (GNUNET_YES == in_shutdown))
1058 do_shutdown (); 1184 do_shutdown ();
1059} 1185 else if (GNUNET_YES == in_shutdown)
1060 1186 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1187 "Delaying shutdown after child's death, still have %u children\n",
1188 list_count (running_head));
1061 1189
1062/**
1063 * Handler for SHUTDOWN message.
1064 *
1065 * @param cls closure (refers to service)
1066 * @param client identification of the client
1067 * @param message the actual message
1068 */
1069static void
1070handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client,
1071 const struct GNUNET_MessageHeader *message)
1072{
1073 GNUNET_SCHEDULER_shutdown ();
1074 GNUNET_SERVER_client_persist_ (client);
1075} 1190}
1076 1191
1077 1192
@@ -1174,6 +1289,43 @@ setup_service (void *cls, const char *section)
1174 1289
1175 1290
1176/** 1291/**
1292 * A client connected, add it to the notification context.
1293 *
1294 * @param cls closure
1295 * @param client identification of the client
1296 */
1297static void
1298handle_client_connecting (void *cls, struct GNUNET_SERVER_Client *client)
1299{
1300 /* All clients are considered to be of the "monitor" kind
1301 * (that is, they don't affect ARM shutdown).
1302 */
1303 if (NULL != client)
1304 GNUNET_SERVER_client_mark_monitor (client);
1305}
1306
1307/**
1308 * Handle MONITOR-message.
1309 *
1310 * @param cls closure (always NULL)
1311 * @param client identification of the client
1312 * @param message the actual message
1313 * @return GNUNET_OK to keep the connection open,
1314 * GNUNET_SYSERR to close it (signal serious error)
1315 */
1316static void
1317handle_monitor (void *cls, struct GNUNET_SERVER_Client *client,
1318 const struct GNUNET_MessageHeader *message)
1319{
1320 /* Removal is handled by the server implementation, internally. */
1321 if ((NULL != client) && (NULL != notifier))
1322 {
1323 GNUNET_SERVER_notification_context_add (notifier, client);
1324 broadcast_status ("arm", GNUNET_ARM_SERVICE_MONITORING_STARTED, client);
1325 }
1326}
1327
1328/**
1177 * Process arm requests. 1329 * Process arm requests.
1178 * 1330 *
1179 * @param cls closure 1331 * @param cls closure
@@ -1187,8 +1339,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
1187 static const struct GNUNET_SERVER_MessageHandler handlers[] = { 1339 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
1188 {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0}, 1340 {&handle_start, NULL, GNUNET_MESSAGE_TYPE_ARM_START, 0},
1189 {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0}, 1341 {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP, 0},
1190 {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN, 1342 {&handle_monitor, NULL, GNUNET_MESSAGE_TYPE_ARM_MONITOR, 0},
1191 sizeof (struct GNUNET_MessageHeader)},
1192 {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST, 1343 {&handle_list, NULL, GNUNET_MESSAGE_TYPE_ARM_LIST,
1193 sizeof (struct GNUNET_MessageHeader)}, 1344 sizeof (struct GNUNET_MessageHeader)},
1194 {NULL, NULL, 0, 0} 1345 {NULL, NULL, 0, 0}
@@ -1241,7 +1392,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
1241 continue; 1392 continue;
1242 } 1393 }
1243 sl->is_default = GNUNET_YES; 1394 sl->is_default = GNUNET_YES;
1244 start_process (sl); 1395 start_process (sl, NULL, 0);
1245 } 1396 }
1246 } 1397 }
1247 GNUNET_free (defaultservices); 1398 GNUNET_free (defaultservices);
@@ -1253,6 +1404,9 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv,
1253 ("No default services configured, GNUnet will not really start right now.\n")); 1404 ("No default services configured, GNUnet will not really start right now.\n"));
1254 } 1405 }
1255 1406
1407 notifier =
1408 GNUNET_SERVER_notification_context_create (server, MAX_NOTIFY_QUEUE);
1409 GNUNET_SERVER_connect_notify (server, handle_client_connecting, NULL);
1256 /* process client requests */ 1410 /* process client requests */
1257 GNUNET_SERVER_add_handlers (server, handlers); 1411 GNUNET_SERVER_add_handlers (server, handlers);
1258} 1412}
diff --git a/src/arm/mockup-service.c b/src/arm/mockup-service.c
index d9896997b..2e021627e 100644
--- a/src/arm/mockup-service.c
+++ b/src/arm/mockup-service.c
@@ -29,21 +29,27 @@
29#include "gnunet_time_lib.h" 29#include "gnunet_time_lib.h"
30 30
31 31
32static int special_ret = 0;
33
32/** 34/**
33 * Handler for SHUTDOWN message. 35 * Handler for STOP message.
34 * 36 *
35 * @param cls closure (refers to service) 37 * @param cls closure (refers to service)
36 * @param client identification of the client 38 * @param client identification of the client
37 * @param message the actual message 39 * @param message the actual message
38 */ 40 */
39static void 41static void
40handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client, 42handle_stop (void *cls, struct GNUNET_SERVER_Client *client,
41 const struct GNUNET_MessageHeader *message) 43 const struct GNUNET_MessageHeader *message)
42{ 44{
43 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 45 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
44 _("Initiating shutdown as requested by client.\n")); 46 _("Initiating shutdown as requested by client.\n"));
45 GNUNET_SERVER_client_persist_ (client); 47 GNUNET_SERVER_client_persist_ (client);
46 GNUNET_SCHEDULER_shutdown (); 48 GNUNET_SCHEDULER_shutdown ();
49 /* ARM won't exponentially increase restart delay if we
50 * terminate normally. This changes the return code.
51 */
52 special_ret = 1;
47} 53}
48 54
49 55
@@ -52,7 +58,7 @@ run (void *cls, struct GNUNET_SERVER_Handle *server,
52 const struct GNUNET_CONFIGURATION_Handle *cfg) 58 const struct GNUNET_CONFIGURATION_Handle *cfg)
53{ 59{
54 static const struct GNUNET_SERVER_MessageHandler handlers[] = { 60 static const struct GNUNET_SERVER_MessageHandler handlers[] = {
55 {&handle_shutdown, NULL, GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN, 61 {&handle_stop, NULL, GNUNET_MESSAGE_TYPE_ARM_STOP,
56 sizeof (struct GNUNET_MessageHeader)}, 62 sizeof (struct GNUNET_MessageHeader)},
57 {NULL, NULL, 0, 0} 63 {NULL, NULL, 0, 0}
58 }; 64 };
@@ -70,5 +76,7 @@ main (int argc, char *const *argv)
70 (GNUNET_OK == 76 (GNUNET_OK ==
71 GNUNET_SERVICE_run (argc, argv, "do-nothing", GNUNET_SERVICE_OPTION_NONE, 77 GNUNET_SERVICE_run (argc, argv, "do-nothing", GNUNET_SERVICE_OPTION_NONE,
72 &run, NULL)) ? 0 : 1; 78 &run, NULL)) ? 0 : 1;
79 if (0 != special_ret)
80 return special_ret;
73 return ret; 81 return ret;
74} 82}
diff --git a/src/arm/test_arm_api.c b/src/arm/test_arm_api.c
index 366d4e500..999bd77e6 100644
--- a/src/arm/test_arm_api.c
+++ b/src/arm/test_arm_api.c
@@ -42,77 +42,144 @@ static struct GNUNET_ARM_Handle *arm;
42 42
43static int ok = 1; 43static int ok = 1;
44 44
45static int phase = 0;
46
45static void 47static void
46arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success) 48arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
47{ 49{
48 GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN); 50 /* (6), a stop request should be sent to ARM successfully */
49 if (success != GNUNET_ARM_PROCESS_DOWN) 51 /* ARM should report that it is stopping */
50 ok = 3; 52 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
51 else if (ok == 1) 53 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING);
52 ok = 0; 54 GNUNET_break (phase == 6);
55 phase++;
56 FPRINTF (stderr, "Sent 'STOP' request for arm to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
53} 57}
54 58
55
56static void 59static void
57arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus success) 60resolver_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
58{ 61{
59 GNUNET_break (success == GNUNET_ARM_PROCESS_DOWN); 62 /* (5), a stop request should be sent to ARM successfully.
63 * ARM should report that resolver is stopped.
64 */
65 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
66 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPED);
67 GNUNET_break (phase == 5);
68 FPRINTF (stderr, "Sent 'STOP' request for resolver to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
69 phase++;
60#if START_ARM 70#if START_ARM
61 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); 71 GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
72#else
73 arm_stop_cb (NULL, GNUNET_ARM_STATUS_SENT_OK, "arm", GNUNET_ARM_SERVICE_STOPPING);
74 arm_conn (NULL, GNUNET_NO, GNUNET_NO);
62#endif 75#endif
63} 76}
64 77
65
66static void 78static void
67dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen) 79dns_notify (void *cls, const struct sockaddr *addr, socklen_t addrlen)
68{ 80{
69 if (addr == NULL) 81 if (addr == NULL)
70 { 82 {
83 /* (4), resolver should finish resolving localhost */
84 GNUNET_break (phase == 4);
85 phase++;
86 FPRINTF (stderr, "%s", "Finished resolving localhost\n");
71 if (ok != 0) 87 if (ok != 0)
72 { 88 ok = 2;
73 GNUNET_break (0); 89 GNUNET_ARM_request_service_stop (arm, "resolver", TIMEOUT, resolver_stop_cb, NULL);
74 ok = 2;
75 }
76 GNUNET_ARM_stop_service (arm, "resolver", TIMEOUT, &arm_notify_stop,
77 NULL);
78 return; 90 return;
79 } 91 }
92 /* (3), resolver should resolve localhost */
93 GNUNET_break (phase == 3);
94 FPRINTF (stderr, "%s", "Resolved localhost\n");
95 phase++;
80 GNUNET_break (addr != NULL); 96 GNUNET_break (addr != NULL);
81 ok = 0; 97 ok = 0;
82} 98}
83 99
84
85static void 100static void
86resolver_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) 101resolver_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
87{ 102{
88 if (success != GNUNET_ARM_PROCESS_STARTING) 103 /* (2), the start request for resolver should be sent successfully
89 { 104 * ARM should report that resolver service is starting.
90 GNUNET_break (0); 105 */
91 ok = 2; 106 GNUNET_assert (status == GNUNET_ARM_REQUEST_SENT_OK);
92#if START_ARM 107 GNUNET_break (phase == 2);
93 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); 108 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
94#endif 109 FPRINTF (stderr, "Sent 'START' request for resolver to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
95 return; 110 phase++;
96 }
97 GNUNET_RESOLVER_ip_get ("localhost", AF_INET, TIMEOUT, &dns_notify, NULL); 111 GNUNET_RESOLVER_ip_get ("localhost", AF_INET, TIMEOUT, &dns_notify, NULL);
98} 112}
99 113
100
101static void 114static void
102arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) 115trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
103{ 116{
104 if (success != GNUNET_ARM_PROCESS_STARTING) 117 GNUNET_ARM_disconnect ((struct GNUNET_ARM_Handle *) cls);
105 { 118}
106 GNUNET_break (0); 119
107 ok = 2; 120
121void
122arm_conn (void *cls, struct GNUNET_ARM_Handle *arm, unsigned char connected, unsigned char error)
123{
124 if (GNUNET_YES == error)
125 {
126 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
127 _("Fatal error initializing ARM API.\n"));
128 GNUNET_SCHEDULER_shutdown ();
129 GNUNET_assert (0);
130 return;
131 }
132 if (connected)
133 {
134 /* (1), arm connection should be established */
135 FPRINTF (stderr, "%s", "Connected to ARM\n");
136 GNUNET_break (phase == 1);
137 phase++;
138 GNUNET_ARM_request_service_start (arm, "resolver", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, resolver_start_cb, NULL);
139 }
140 else
141 {
142 /* (7), ARM should stop (we disconnect from it) */
143 FPRINTF (stderr, "%s", "Disconnected from ARM\n");
144 GNUNET_break (phase == 7);
145 if (phase != 7)
146 ok = 3;
147 else if (ok == 1)
148 ok = 0;
149 GNUNET_SCHEDULER_add_now (trigger_disconnect, arm);
150 }
151}
152
153void
154srv_status (void *cls, const char *service, enum GNUNET_ARM_ServiceStatus status)
155{
156 FPRINTF (stderr, "Service %s is %u\n", service, status);
157 switch (phase)
158 {
159 default:
160 FPRINTF (stderr, "Unexpectedly got status %u for service %s\n", service);
161 GNUNET_break (0);
162 ok = 2;
108#if START_ARM 163#if START_ARM
109 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); 164 GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, NULL, NULL);
110#endif 165#endif
111 } 166 }
112 GNUNET_ARM_start_service (arm, "resolver", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, &resolver_notify,
113 NULL);
114} 167}
115 168
169static void
170arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
171{
172 /* (0) The request should be "sent" successfully
173 * ("sent", because it isn't going anywhere, ARM API starts ARM service
174 * by itself).
175 * ARM API should report that ARM service is starting.
176 */
177 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
178 GNUNET_break (phase == 0);
179 FPRINTF (stderr, "Sent 'START' request for arm to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
180 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
181 phase++;
182}
116 183
117static void 184static void
118task (void *cls, char *const *args, const char *cfgfile, 185task (void *cls, char *const *args, const char *cfgfile,
@@ -133,11 +200,13 @@ task (void *cls, char *const *args, const char *cfgfile,
133 else 200 else
134 GNUNET_free (armconfig); 201 GNUNET_free (armconfig);
135 } 202 }
136 arm = GNUNET_ARM_connect (cfg, NULL); 203 arm = GNUNET_ARM_alloc (cfg);
204 GNUNET_ARM_connect (arm, arm_conn, NULL);
137#if START_ARM 205#if START_ARM
138 GNUNET_ARM_start_service (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, &arm_notify, NULL); 206 GNUNET_ARM_request_service_start (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, arm_start_cb, NULL);
139#else 207#else
140 arm_notify (NULL, GNUNET_YES); 208 arm_start_cb (NULL, arm, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_RESULT_STARTING);
209 arm_conn (NULL, GNUNET_YES, GNUNET_NO);
141#endif 210#endif
142} 211}
143 212
diff --git a/src/arm/test_exponential_backoff.c b/src/arm/test_exponential_backoff.c
index 3395139ea..8a0dba117 100644
--- a/src/arm/test_exponential_backoff.c
+++ b/src/arm/test_exponential_backoff.c
@@ -43,14 +43,20 @@ static const struct GNUNET_CONFIGURATION_Handle *cfg;
43 43
44static struct GNUNET_ARM_Handle *arm; 44static struct GNUNET_ARM_Handle *arm;
45 45
46static struct GNUNET_ARM_MonitorHandle *mon;
47
46static int ok = 1; 48static int ok = 1;
47 49
50static int phase = 0;
51
48static int trialCount; 52static int trialCount;
49 53
50static struct GNUNET_TIME_Absolute startedWaitingAt; 54static struct GNUNET_TIME_Absolute startedWaitingAt;
51 55
52struct GNUNET_TIME_Relative waitedFor; 56struct GNUNET_TIME_Relative waitedFor;
53 57
58struct GNUNET_TIME_Relative waitedFor_prev;
59
54#if LOG_BACKOFF 60#if LOG_BACKOFF
55static FILE *killLogFilePtr; 61static FILE *killLogFilePtr;
56 62
@@ -97,11 +103,8 @@ struct ShutdownContext
97 103
98/** 104/**
99 * Handler receiving response to service shutdown requests. 105 * Handler receiving response to service shutdown requests.
100 * First call with NULL: service misbehaving, or something. 106 * We expect it to be called with NULL, since the service that
101 * First call with GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN: 107 * we are shutting down will just die without replying.
102 * - service will shutdown
103 * Second call with NULL:
104 * - service has now really shut down.
105 * 108 *
106 * @param cls closure 109 * @param cls closure
107 * @param msg NULL, indicating socket closure. 110 * @param msg NULL, indicating socket closure.
@@ -111,7 +114,7 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
111{ 114{
112 struct ShutdownContext *shutdown_ctx = cls; 115 struct ShutdownContext *shutdown_ctx = cls;
113 116
114 if (msg == NULL) 117 if (NULL == msg)
115 { 118 {
116 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n"); 119 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service shutdown complete.\n");
117 if (shutdown_ctx->cont != NULL) 120 if (shutdown_ctx->cont != NULL)
@@ -122,29 +125,7 @@ service_shutdown_handler (void *cls, const struct GNUNET_MessageHeader *msg)
122 GNUNET_free (shutdown_ctx); 125 GNUNET_free (shutdown_ctx);
123 return; 126 return;
124 } 127 }
125 GNUNET_assert (ntohs (msg->size) == 128 GNUNET_assert (0);
126 sizeof (struct GNUNET_MessageHeader));
127 switch (ntohs (msg->type))
128 {
129 case GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN:
130 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
131 "Received confirmation for service shutdown.\n");
132 shutdown_ctx->confirmed = GNUNET_YES;
133 GNUNET_CLIENT_receive (shutdown_ctx->sock,
134 &service_shutdown_handler, shutdown_ctx,
135 GNUNET_TIME_UNIT_FOREVER_REL);
136 break;
137 default: /* Fall through */
138 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
139 "Service shutdown refused!\n");
140 if (shutdown_ctx->cont != NULL)
141 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_YES);
142
143 GNUNET_SCHEDULER_cancel (shutdown_ctx->cancel_task);
144 GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
145 GNUNET_free (shutdown_ctx);
146 break;
147 }
148} 129}
149 130
150 131
@@ -183,25 +164,27 @@ write_shutdown (void *cls, size_t size, void *buf)
183 struct ShutdownContext *shutdown_ctx = cls; 164 struct ShutdownContext *shutdown_ctx = cls;
184 165
185 if (size < sizeof (struct GNUNET_MessageHeader)) 166 if (size < sizeof (struct GNUNET_MessageHeader))
186 { 167 {
187 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 168 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
188 _("Failed to transmit shutdown request to client.\n")); 169 _("Failed to transmit shutdown request to client.\n"));
189 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR); 170 FPRINTF (stderr, "%s", "Failed to send a shutdown request\n");
190 GNUNET_CLIENT_disconnect (shutdown_ctx->sock); 171 shutdown_ctx->cont (shutdown_ctx->cont_cls, GNUNET_SYSERR);
191 GNUNET_free (shutdown_ctx); 172 GNUNET_CLIENT_disconnect (shutdown_ctx->sock);
192 return 0; /* client disconnected */ 173 GNUNET_free (shutdown_ctx);
193 } 174 return 0; /* client disconnected */
175 }
194 176
195 GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler, 177 GNUNET_CLIENT_receive (shutdown_ctx->sock, &service_shutdown_handler,
196 shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL); 178 shutdown_ctx, GNUNET_TIME_UNIT_FOREVER_REL);
197 shutdown_ctx->cancel_task = 179 shutdown_ctx->cancel_task = GNUNET_SCHEDULER_add_delayed (
198 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_absolute_get_remaining 180 GNUNET_TIME_absolute_get_remaining (shutdown_ctx->timeout),
199 (shutdown_ctx->timeout), 181 &service_shutdown_cancel, shutdown_ctx);
200 &service_shutdown_cancel, shutdown_ctx);
201 msg = (struct GNUNET_MessageHeader *) buf; 182 msg = (struct GNUNET_MessageHeader *) buf;
202 msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN); 183 msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_STOP);
203 msg->size = htons (sizeof (struct GNUNET_MessageHeader)); 184 msg->size = htons (sizeof (struct GNUNET_MessageHeader));
204 return sizeof (struct GNUNET_MessageHeader); 185 strcpy ((char *) &msg[1], "do-nothing");
186 FPRINTF (stderr, "%s", "Sent a shutdown request\n");
187 return sizeof (struct GNUNET_MessageHeader) + strlen ("do-nothing") + 1;
205} 188}
206 189
207 190
@@ -219,7 +202,7 @@ write_shutdown (void *cls, size_t size, void *buf)
219 * 202 *
220 */ 203 */
221static void 204static void
222arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock, 205do_nothing_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
223 struct GNUNET_TIME_Relative timeout, 206 struct GNUNET_TIME_Relative timeout,
224 GNUNET_CLIENT_ShutdownTask cont, void *cont_cls) 207 GNUNET_CLIENT_ShutdownTask cont, void *cont_cls)
225{ 208{
@@ -231,125 +214,132 @@ arm_service_shutdown (struct GNUNET_CLIENT_Connection *sock,
231 shutdown_ctx->sock = sock; 214 shutdown_ctx->sock = sock;
232 shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout); 215 shutdown_ctx->timeout = GNUNET_TIME_relative_to_absolute (timeout);
233 GNUNET_CLIENT_notify_transmit_ready (sock, 216 GNUNET_CLIENT_notify_transmit_ready (sock,
234 sizeof (struct GNUNET_MessageHeader), 217 sizeof (struct GNUNET_MessageHeader) + strlen ("do-nothing") + 1,
235 timeout, GNUNET_NO, &write_shutdown, 218 timeout, GNUNET_NO, &write_shutdown,
236 shutdown_ctx); 219 shutdown_ctx);
237} 220}
238 221
239
240static void
241arm_notify_stop (void *cls, enum GNUNET_ARM_ProcessStatus status)
242{
243 GNUNET_assert ( (status == GNUNET_ARM_PROCESS_DOWN) ||
244 (status == GNUNET_ARM_PROCESS_ALREADY_DOWN) );
245#if START_ARM
246 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, NULL, NULL);
247#endif
248}
249
250
251static void 222static void
252kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc); 223kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc);
253 224
254
255static void 225static void
256do_nothing_notify (void *cls, enum GNUNET_ARM_ProcessStatus status) 226shutdown_cont (void *cls, int reason)
257{
258 GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING);
259 ok = 1;
260 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &kill_task, NULL);
261}
262
263static void
264arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus status)
265{ 227{
266 GNUNET_assert (status == GNUNET_ARM_PROCESS_STARTING); 228 if (GNUNET_NO != reason)
267 GNUNET_ARM_start_service (arm, "do-nothing", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, TIMEOUT, &do_nothing_notify, 229 {
268 NULL); 230 /* Re-try shutdown */
231 FPRINTF (stderr, "%s", "do-nothing didn't die, trying again\n");
232 GNUNET_SCHEDULER_add_now (kill_task, NULL);
233 return;
234 }
235 startedWaitingAt = GNUNET_TIME_absolute_get ();
236 FPRINTF (stderr, "%s", "do-nothing is dead, starting the countdown\n");
269} 237}
270 238
271
272static void 239static void
273kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc); 240kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc)
274
275
276static void
277do_nothing_restarted_notify_task (void *cls,
278 const struct GNUNET_SCHEDULER_TaskContext
279 *tc)
280{ 241{
281 static char a; 242 static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL;
282
283 trialCount++;
284 243
244 if (NULL != cbData)
245 {
246 waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt);
247 FPRINTF (stderr, "Waited for: %llu ms\n", waitedFor.rel_value);
285#if LOG_BACKOFF 248#if LOG_BACKOFF
286 if ((tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN) != 0) 249 FPRINTF (killLogFilePtr, "Waited for: %llu ms\n",
287 { 250 (unsigned long long) waitedFor.rel_value);
288 FPRINTF (killLogFilePtr, "%d.Reason is shutdown!\n", trialCount);
289 }
290 else if ((tc->reason & GNUNET_SCHEDULER_REASON_TIMEOUT) != 0)
291 {
292 FPRINTF (killLogFilePtr, "%d.Reason is timeout!\n", trialCount);
293 }
294 else if ((tc->reason & GNUNET_SCHEDULER_REASON_PREREQ_DONE) != 0)
295 {
296 FPRINTF (killLogFilePtr, "%d.Service is running!\n", trialCount);
297 }
298#endif 251#endif
299 GNUNET_SCHEDULER_add_now (&kill_task, &a); 252 }
253 else
254 {
255 waitedFor.rel_value = 0;
256 }
257 /* Connect to the doNothing task */
258 doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg);
259 GNUNET_assert (doNothingConnection != NULL);
260 if (trialCount == 12)
261 waitedFor_prev = waitedFor;
262 else if (trialCount == 13)
263 {
264 GNUNET_CLIENT_disconnect (doNothingConnection);
265 GNUNET_ARM_request_service_stop (arm, "do-nothing", TIMEOUT, NULL, NULL);
266 if (waitedFor_prev.rel_value >= waitedFor.rel_value)
267 ok = 9;
268 else
269 ok = 0;
270 trialCount += 1;
271 return;
272 }
273 trialCount += 1;
274 /* Use the created connection to kill the doNothingTask */
275 do_nothing_service_shutdown (doNothingConnection,
276 TIMEOUT, &shutdown_cont, NULL);
300} 277}
301 278
302
303static void 279static void
304do_test (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc) 280trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
305{ 281{
306 GNUNET_CLIENT_service_test ("do-nothing", cfg, TIMEOUT, 282 GNUNET_ARM_disconnect (arm);
307 &do_nothing_restarted_notify_task, NULL); 283 GNUNET_ARM_monitor_disconnect (mon);
308} 284}
309 285
310 286
311static void 287static void
312shutdown_cont (void *cls, int reason) 288arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
313{ 289{
314 trialCount++; 290 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
315 startedWaitingAt = GNUNET_TIME_absolute_get (); 291 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING);
316 GNUNET_SCHEDULER_add_delayed (waitedFor, &do_test, NULL); 292 FPRINTF (stderr, "%s", "ARM service stopped\n");
293 GNUNET_SCHEDULER_add_now (trigger_disconnect, NULL);
317} 294}
318 295
319 296void
320static void 297srv_status (void *cls, struct GNUNET_ARM_MonitorHandle *mon, const char *service, enum GNUNET_ARM_ServiceStatus status)
321kill_task (void *cbData, const struct GNUNET_SCHEDULER_TaskContext *tc)
322{ 298{
323 static struct GNUNET_CLIENT_Connection *doNothingConnection = NULL; 299 FPRINTF (stderr, "Service %s is %u, phase %u\n", service, status, phase);
324 300 if (status == GNUNET_ARM_SERVICE_MONITORING_STARTED)
325 if (NULL != cbData) 301 {
326 { 302 phase++;
327 waitedFor = GNUNET_TIME_absolute_get_duration (startedWaitingAt); 303 GNUNET_ARM_request_service_start (arm, "do-nothing",
328 304 GNUNET_OS_INHERIT_STD_OUT_AND_ERR, TIMEOUT, NULL, NULL);
329#if LOG_BACKOFF 305 return;
330 FPRINTF (killLogFilePtr, "Waited for: %llu ms\n", 306 }
331 (unsigned long long) waitedFor.rel_value); 307 if (phase == 1)
332#endif 308 {
333 } 309 GNUNET_break (status == GNUNET_ARM_SERVICE_STARTING);
334 else 310 GNUNET_break (0 == strcasecmp (service, "do-nothing"));
311 GNUNET_break (phase == 1);
312 FPRINTF (stderr, "%s", "do-nothing is starting\n");
313 phase++;
314 ok = 1;
315 GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_SECONDS, &kill_task, NULL);
316 }
317 else if ((phase == 2) && (strcasecmp ("do-nothing", service) == 0))
318 {
319 /* We passively monitor ARM for status updates. ARM should tell us
320 * when do-nothing dies (no need to run a service upness test ourselves).
321 */
322 if (status == GNUNET_ARM_SERVICE_STARTING)
335 { 323 {
336 waitedFor.rel_value = 0; 324 FPRINTF (stderr, "%s", "do-nothing is starting\n");
325 GNUNET_SCHEDULER_add_now (kill_task, &ok);
337 } 326 }
338 /* Connect to the doNothing task */ 327 else if ((status == GNUNET_ARM_SERVICE_STOPPED) && (trialCount == 14))
339 doNothingConnection = GNUNET_CLIENT_connect ("do-nothing", cfg);
340 GNUNET_assert (doNothingConnection != NULL);
341 if (trialCount == 12)
342 { 328 {
343 GNUNET_CLIENT_disconnect (doNothingConnection); 329 phase++;
344 GNUNET_ARM_stop_service (arm, "do-nothing", TIMEOUT, &arm_notify_stop, 330 GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
345 NULL);
346 ok = 0;
347 return;
348 } 331 }
349 /* Use the created connection to kill the doNothingTask */ 332 }
350 arm_service_shutdown (doNothingConnection, TIMEOUT, &shutdown_cont, NULL);
351} 333}
352 334
335static void
336arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
337{
338 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
339 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
340 GNUNET_break (phase == 0);
341 FPRINTF (stderr, "Sent 'START' request for arm to ARM %s\n", (status == GNUNET_ARM_REQUEST_SENT_OK) ? "successfully" : "unsuccessfully");
342}
353 343
354static void 344static void
355task (void *cls, char *const *args, const char *cfgfile, 345task (void *cls, char *const *args, const char *cfgfile,
@@ -359,9 +349,8 @@ task (void *cls, char *const *args, const char *cfgfile,
359 cfg = c; 349 cfg = c;
360 if (NULL != cfgfile) 350 if (NULL != cfgfile)
361 { 351 {
362 if (GNUNET_OK != 352 if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_filename (cfg, "arm",
363 GNUNET_CONFIGURATION_get_value_filename (cfg, "arm", "CONFIG", 353 "CONFIG", &armconfig))
364 &armconfig))
365 { 354 {
366 GNUNET_CONFIGURATION_set_value_string ((struct GNUNET_CONFIGURATION_Handle 355 GNUNET_CONFIGURATION_set_value_string ((struct GNUNET_CONFIGURATION_Handle
367 *) cfg, "arm", "CONFIG", 356 *) cfg, "arm", "CONFIG",
@@ -371,16 +360,18 @@ task (void *cls, char *const *args, const char *cfgfile,
371 GNUNET_free (armconfig); 360 GNUNET_free (armconfig);
372 } 361 }
373 362
374 arm = GNUNET_ARM_connect (cfg, NULL); 363 arm = GNUNET_ARM_alloc (cfg);
364 GNUNET_ARM_connect (arm, NULL, NULL);
365 mon = GNUNET_ARM_monitor_alloc (cfg);
366 GNUNET_ARM_monitor (mon, srv_status, NULL);
375#if START_ARM 367#if START_ARM
376 GNUNET_ARM_start_service (arm, "arm", GNUNET_OS_INHERIT_STD_OUT_AND_ERR, GNUNET_TIME_UNIT_ZERO, &arm_notify, 368 GNUNET_ARM_request_service_start (arm, "arm",
377 NULL); 369 GNUNET_OS_INHERIT_STD_OUT_AND_ERR, GNUNET_TIME_UNIT_ZERO, arm_start_cb, NULL);
378#else 370#else
379 arm_do_nothing (NULL, GNUNET_YES); 371 arm_start_cb (NULL, arm, GNUNET_ARM_REQUEST_SENT_OK, "arm", GNUNET_ARM_SERVICE_STARTING);
380#endif 372#endif
381} 373}
382 374
383
384static int 375static int
385check () 376check ()
386{ 377{
diff --git a/src/arm/test_gnunet_service_arm.c b/src/arm/test_gnunet_service_arm.c
index 9e9286ac1..bd7fe5fa9 100644
--- a/src/arm/test_gnunet_service_arm.c
+++ b/src/arm/test_gnunet_service_arm.c
@@ -42,23 +42,22 @@ static int ret = 1;
42 42
43static struct GNUNET_ARM_Handle *arm; 43static struct GNUNET_ARM_Handle *arm;
44 44
45
46static void 45static void
47arm_stopped (void *cls, enum GNUNET_ARM_ProcessStatus success) 46trigger_disconnect (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
48{ 47{
49 if (success != GNUNET_ARM_PROCESS_DOWN)
50 {
51 GNUNET_break (0);
52 ret = 4;
53 }
54 else
55 {
56 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "ARM stopped\n");
57 }
58 GNUNET_ARM_disconnect (arm); 48 GNUNET_ARM_disconnect (arm);
59 arm = NULL; 49 arm = NULL;
60} 50}
61 51
52static void
53arm_stop_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
54{
55 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
56 GNUNET_break (result == GNUNET_ARM_RESULT_STOPPING);
57 if (result != GNUNET_ARM_RESULT_STOPPING)
58 ret = 4;
59 GNUNET_SCHEDULER_add_now (trigger_disconnect, NULL);
60}
62 61
63static void 62static void
64hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen) 63hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen)
@@ -66,43 +65,37 @@ hostNameResolveCB (void *cls, const struct sockaddr *addr, socklen_t addrlen)
66 if ((ret == 0) || (ret == 4)) 65 if ((ret == 0) || (ret == 4))
67 return; 66 return;
68 if (NULL == addr) 67 if (NULL == addr)
69 { 68 {
70 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n"); 69 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Name not resolved!\n");
71 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); 70 ret = 3;
72 ret = 3; 71 }
73 return; 72 else
74 } 73 {
75 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 74 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
76 "Resolved hostname, now stopping ARM\n"); 75 "Resolved hostname, now stopping ARM\n");
77 ret = 0; 76 ret = 0;
78 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); 77 }
78 GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
79} 79}
80 80
81
82static void 81static void
83arm_notify (void *cls, enum GNUNET_ARM_ProcessStatus success) 82arm_start_cb (void *cls, struct GNUNET_ARM_Handle *h, enum GNUNET_ARM_RequestStatus status, const char *servicename, enum GNUNET_ARM_Result result)
84{ 83{
85 if (success != GNUNET_ARM_PROCESS_STARTING) 84 GNUNET_break (status == GNUNET_ARM_REQUEST_SENT_OK);
86 { 85 GNUNET_break (result == GNUNET_ARM_RESULT_STARTING);
87 GNUNET_break (0);
88 ret = 1;
89 return;
90 }
91 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 86 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
92 "Trying to resolve our own hostname!\n"); 87 "Trying to resolve our own hostname!\n");
93 /* connect to the resolver service */ 88 /* connect to the resolver service */
94 if (NULL == 89 if (NULL == GNUNET_RESOLVER_hostname_resolve (
95 GNUNET_RESOLVER_hostname_resolve (AF_UNSPEC, TIMEOUT, 90 AF_UNSPEC, TIMEOUT, &hostNameResolveCB, NULL))
96 &hostNameResolveCB, NULL)) 91 {
97 { 92 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
98 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 93 "Unable initiate connection to resolver service\n");
99 "Unable initiate connection to resolver service\n"); 94 ret = 2;
100 ret = 2; 95 GNUNET_ARM_request_service_stop (arm, "arm", TIMEOUT, arm_stop_cb, NULL);
101 GNUNET_ARM_stop_service (arm, "arm", TIMEOUT, &arm_stopped, NULL); 96 }
102 }
103} 97}
104 98
105
106static void 99static void
107run (void *cls, char *const *args, const char *cfgfile, 100run (void *cls, char *const *args, const char *cfgfile,
108 const struct GNUNET_CONFIGURATION_Handle *c) 101 const struct GNUNET_CONFIGURATION_Handle *c)
@@ -122,10 +115,10 @@ run (void *cls, char *const *args, const char *cfgfile,
122 else 115 else
123 GNUNET_free (armconfig); 116 GNUNET_free (armconfig);
124 } 117 }
125 arm = GNUNET_ARM_connect (c, NULL); 118 arm = GNUNET_ARM_alloc (c);
126 GNUNET_ARM_start_service (arm, "arm", 119 GNUNET_ARM_connect (arm, NULL, NULL);
127 GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, 120 GNUNET_ARM_request_service_start (arm, "arm",
128 &arm_notify, NULL); 121 GNUNET_OS_INHERIT_STD_OUT_AND_ERR, START_TIMEOUT, arm_start_cb, NULL);
129} 122}
130 123
131 124