diff options
Diffstat (limited to 'src/arm/gnunet-service-arm.c')
-rw-r--r-- | src/arm/gnunet-service-arm.c | 1466 |
1 files changed, 630 insertions, 836 deletions
diff --git a/src/arm/gnunet-service-arm.c b/src/arm/gnunet-service-arm.c index 74bbd76c2..733eac6c6 100644 --- a/src/arm/gnunet-service-arm.c +++ b/src/arm/gnunet-service-arm.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 Christian Grothoff (and other contributing authors) | 3 | (C) 2009, 2010, 2011 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 |
@@ -22,31 +22,18 @@ | |||
22 | * @file arm/gnunet-service-arm.c | 22 | * @file arm/gnunet-service-arm.c |
23 | * @brief the automated restart manager service | 23 | * @brief the automated restart manager service |
24 | * @author Christian Grothoff | 24 | * @author Christian Grothoff |
25 | * | ||
26 | * TODO: | ||
27 | * - need to test auto-restart code on configuration changes; | ||
28 | * - should refine restart code to check if *relevant* parts of the | ||
29 | * configuration were changed (anything in the section for the service) | ||
30 | * - should have a way to specify dependencies between services and | ||
31 | * manage restarts of groups of services | ||
32 | * | ||
33 | * + install handler for disconnecting clients!? | ||
34 | */ | 25 | */ |
35 | #include "platform.h" | 26 | #include "platform.h" |
36 | #include "gnunet_util_lib.h" | 27 | #include "gnunet_util_lib.h" |
28 | #include "gnunet_arm_service.h" | ||
37 | #include "gnunet_protocols.h" | 29 | #include "gnunet_protocols.h" |
38 | #include "arm.h" | 30 | #include "arm.h" |
39 | 31 | ||
40 | |||
41 | /** | ||
42 | * Check for configuration file changes every 5s. | ||
43 | */ | ||
44 | #define MAINT_FREQUENCY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 5) | ||
45 | |||
46 | /** | 32 | /** |
47 | * Threshold after which exponential backoff shouldn't increase (in ms); 30m | 33 | * Threshold after which exponential backoff shouldn't increase (in ms); 30m |
48 | */ | 34 | */ |
49 | #define EXPONENTIAL_BACKOFF_THRESHOLD (1000 * 60 * 30) | 35 | #define EXPONENTIAL_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 30) |
36 | |||
50 | 37 | ||
51 | /** | 38 | /** |
52 | * List of our services. | 39 | * List of our services. |
@@ -55,6 +42,49 @@ struct ServiceList; | |||
55 | 42 | ||
56 | 43 | ||
57 | /** | 44 | /** |
45 | * Record with information about a listen socket we have open. | ||
46 | */ | ||
47 | struct ServiceListeningInfo | ||
48 | { | ||
49 | /** | ||
50 | * This is a linked list. | ||
51 | */ | ||
52 | struct ServiceListeningInfo *next; | ||
53 | |||
54 | /** | ||
55 | * This is a linked list. | ||
56 | */ | ||
57 | struct ServiceListeningInfo *prev; | ||
58 | |||
59 | /** | ||
60 | * Address this socket is listening on. | ||
61 | */ | ||
62 | struct sockaddr *service_addr; | ||
63 | |||
64 | /** | ||
65 | * Service this listen socket is for. | ||
66 | */ | ||
67 | struct ServiceList *sl; | ||
68 | |||
69 | /** | ||
70 | * Number of bytes in 'service_addr' | ||
71 | */ | ||
72 | socklen_t service_addr_len; | ||
73 | |||
74 | /** | ||
75 | * Our listening socket. | ||
76 | */ | ||
77 | struct GNUNET_NETWORK_Handle *listen_socket; | ||
78 | |||
79 | /** | ||
80 | * Task doing the accepting. | ||
81 | */ | ||
82 | GNUNET_SCHEDULER_TaskIdentifier accept_task; | ||
83 | |||
84 | }; | ||
85 | |||
86 | |||
87 | /** | ||
58 | * List of our services. | 88 | * List of our services. |
59 | */ | 89 | */ |
60 | struct ServiceList | 90 | struct ServiceList |
@@ -70,6 +100,16 @@ struct ServiceList | |||
70 | struct ServiceList *prev; | 100 | struct ServiceList *prev; |
71 | 101 | ||
72 | /** | 102 | /** |
103 | * Linked list of listen sockets associated with this service. | ||
104 | */ | ||
105 | struct ServiceListeningInfo *listen_head; | ||
106 | |||
107 | /** | ||
108 | * Linked list of listen sockets associated with this service. | ||
109 | */ | ||
110 | struct ServiceListeningInfo *listen_tail; | ||
111 | |||
112 | /** | ||
73 | * Name of the service. | 113 | * Name of the service. |
74 | */ | 114 | */ |
75 | char *name; | 115 | char *name; |
@@ -96,12 +136,6 @@ struct ServiceList | |||
96 | struct GNUNET_OS_Process *proc; | 136 | struct GNUNET_OS_Process *proc; |
97 | 137 | ||
98 | /** | 138 | /** |
99 | * Last time the config of this service was | ||
100 | * modified. | ||
101 | */ | ||
102 | time_t mtime; | ||
103 | |||
104 | /** | ||
105 | * Process exponential backoff time | 139 | * Process exponential backoff time |
106 | */ | 140 | */ |
107 | struct GNUNET_TIME_Relative backoff; | 141 | struct GNUNET_TIME_Relative backoff; |
@@ -109,7 +143,14 @@ struct ServiceList | |||
109 | /** | 143 | /** |
110 | * Absolute time at which the process is scheduled to restart in case of death | 144 | * Absolute time at which the process is scheduled to restart in case of death |
111 | */ | 145 | */ |
112 | struct GNUNET_TIME_Absolute restartAt; | 146 | struct GNUNET_TIME_Absolute restart_at; |
147 | |||
148 | /** | ||
149 | * Is this service to be started by default (or did a client tell us explicitly | ||
150 | * to start it)? GNUNET_NO if the service is started only upon 'accept' on a | ||
151 | * listen socket or possibly explicitly by a client changing the value. | ||
152 | */ | ||
153 | int is_default; | ||
113 | 154 | ||
114 | }; | 155 | }; |
115 | 156 | ||
@@ -150,84 +191,15 @@ static GNUNET_SCHEDULER_TaskIdentifier child_death_task; | |||
150 | static GNUNET_SCHEDULER_TaskIdentifier child_restart_task; | 191 | static GNUNET_SCHEDULER_TaskIdentifier child_restart_task; |
151 | 192 | ||
152 | /** | 193 | /** |
153 | * | ||
154 | */ | ||
155 | struct ServiceListeningInfo | ||
156 | { | ||
157 | /** | ||
158 | * This is a linked list. | ||
159 | */ | ||
160 | struct ServiceListeningInfo *next; | ||
161 | |||
162 | /** | ||
163 | * This is a linked list. | ||
164 | */ | ||
165 | struct ServiceListeningInfo *prev; | ||
166 | |||
167 | /** | ||
168 | * Name of the service being forwarded. | ||
169 | */ | ||
170 | char *serviceName; | ||
171 | |||
172 | /** | ||
173 | * | ||
174 | */ | ||
175 | struct sockaddr *service_addr; | ||
176 | |||
177 | /** | ||
178 | * | ||
179 | */ | ||
180 | socklen_t service_addr_len; | ||
181 | |||
182 | /** | ||
183 | * Our listening socket. | ||
184 | */ | ||
185 | struct GNUNET_NETWORK_Handle *listeningSocket; | ||
186 | |||
187 | /** | ||
188 | * Task doing the accepting. | ||
189 | */ | ||
190 | GNUNET_SCHEDULER_TaskIdentifier acceptTask; | ||
191 | }; | ||
192 | |||
193 | |||
194 | /** | ||
195 | * Array with the names of the services started by default. | ||
196 | */ | ||
197 | static char **defaultServicesList; | ||
198 | |||
199 | /** | ||
200 | * Size of the defaultServicesList array. | ||
201 | */ | ||
202 | static unsigned int numDefaultServices; | ||
203 | |||
204 | /** | ||
205 | * | ||
206 | */ | ||
207 | static struct ServiceListeningInfo *serviceListeningInfoList_head; | ||
208 | |||
209 | /** | ||
210 | * | ||
211 | */ | ||
212 | static struct ServiceListeningInfo *serviceListeningInfoList_tail; | ||
213 | |||
214 | |||
215 | /** | ||
216 | * Pipe used to communicate shutdown via signal. | 194 | * Pipe used to communicate shutdown via signal. |
217 | */ | 195 | */ |
218 | static struct GNUNET_DISK_PipeHandle *sigpipe; | 196 | static struct GNUNET_DISK_PipeHandle *sigpipe; |
219 | 197 | ||
220 | /** | 198 | /** |
221 | * Reading end of the signal pipe. | ||
222 | */ | ||
223 | static const struct GNUNET_DISK_FileHandle *pr; | ||
224 | |||
225 | /** | ||
226 | * Are we in shutdown mode? | 199 | * Are we in shutdown mode? |
227 | */ | 200 | */ |
228 | static int in_shutdown; | 201 | static int in_shutdown; |
229 | 202 | ||
230 | |||
231 | /** | 203 | /** |
232 | * Handle to our server instance. Our server is a bit special in that | 204 | * Handle to our server instance. Our server is a bit special in that |
233 | * its service is not immediately stopped once we get a shutdown | 205 | * its service is not immediately stopped once we get a shutdown |
@@ -248,10 +220,9 @@ static struct GNUNET_SERVER_Handle *server; | |||
248 | * Actually start the process for the given service. | 220 | * Actually start the process for the given service. |
249 | * | 221 | * |
250 | * @param sl identifies service to start | 222 | * @param sl identifies service to start |
251 | * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL | ||
252 | */ | 223 | */ |
253 | static void | 224 | static void |
254 | start_process (struct ServiceList *sl, const SOCKTYPE *lsocks) | 225 | start_process (struct ServiceList *sl) |
255 | { | 226 | { |
256 | char *loprefix; | 227 | char *loprefix; |
257 | char *options; | 228 | char *options; |
@@ -261,161 +232,100 @@ start_process (struct ServiceList *sl, const SOCKTYPE *lsocks) | |||
261 | int use_debug; | 232 | int use_debug; |
262 | char b; | 233 | char b; |
263 | char *val; | 234 | char *val; |
235 | struct ServiceListeningInfo *sli; | ||
236 | SOCKTYPE *lsocks; | ||
237 | unsigned int ls; | ||
238 | |||
239 | /* calculate listen socket list */ | ||
240 | lsocks = NULL; | ||
241 | ls = 0; | ||
242 | for (sli = sl->listen_head; NULL != sli; sli = sli->next) | ||
243 | { | ||
244 | GNUNET_array_append (lsocks, ls, | ||
245 | GNUNET_NETWORK_get_fd (sli->listen_socket)); | ||
246 | if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK) | ||
247 | { | ||
248 | GNUNET_SCHEDULER_cancel (sli->accept_task); | ||
249 | sli->accept_task = GNUNET_SCHEDULER_NO_TASK; | ||
250 | } | ||
251 | } | ||
252 | #if WINDOWS | ||
253 | GNUNET_array_append (lsocks, ls, INVALID_SOCKET); | ||
254 | #else | ||
255 | GNUNET_array_append (lsocks, ls, -1); | ||
256 | #endif | ||
264 | 257 | ||
265 | /* start service */ | 258 | /* obtain configuration */ |
266 | if (GNUNET_OK != | 259 | if (GNUNET_OK != |
267 | GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX", | 260 | GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "PREFIX", |
268 | &loprefix)) | 261 | &loprefix)) |
269 | loprefix = GNUNET_strdup (prefix_command); | 262 | loprefix = GNUNET_strdup (prefix_command); |
270 | if (GNUNET_OK != | 263 | if (GNUNET_OK != |
271 | GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS", | 264 | GNUNET_CONFIGURATION_get_value_string (cfg, sl->name, "OPTIONS", |
272 | &options)) | 265 | &options)) |
273 | { | ||
274 | options = GNUNET_strdup (final_option); | ||
275 | if (NULL == strstr (options, "%")) | ||
276 | { | 266 | { |
277 | /* replace '{}' with service name */ | 267 | options = GNUNET_strdup (final_option); |
278 | while (NULL != (optpos = strstr (options, "{}"))) | 268 | if (NULL == strstr (options, "%")) |
279 | { | 269 | { |
280 | optpos[0] = '%'; | 270 | /* replace '{}' with service name */ |
281 | optpos[1] = 's'; | 271 | while (NULL != (optpos = strstr (options, "{}"))) |
282 | GNUNET_asprintf (&optpos, options, sl->name); | 272 | { |
283 | GNUNET_free (options); | 273 | optpos[0] = '%'; |
284 | options = optpos; | 274 | optpos[1] = 's'; |
285 | } | 275 | GNUNET_asprintf (&optpos, options, sl->name); |
286 | /* replace '$PATH' with value associated with "PATH" */ | 276 | GNUNET_free (options); |
287 | while (NULL != (optpos = strstr (options, "$"))) | 277 | options = optpos; |
288 | { | 278 | } |
289 | optend = optpos + 1; | 279 | /* replace '$PATH' with value associated with "PATH" */ |
290 | while (isupper ((unsigned char) *optend)) | 280 | while (NULL != (optpos = strstr (options, "$"))) |
291 | optend++; | 281 | { |
292 | b = *optend; | 282 | optend = optpos + 1; |
293 | if ('\0' == b) | 283 | while (isupper ((unsigned char) *optend)) |
294 | next = ""; | 284 | optend++; |
295 | else | 285 | b = *optend; |
296 | next = optend + 1; | 286 | if ('\0' == b) |
297 | *optend = '\0'; | 287 | next = ""; |
298 | if (GNUNET_OK != | 288 | else |
299 | GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", optpos + 1, | 289 | next = optend + 1; |
300 | &val)) | 290 | *optend = '\0'; |
301 | val = GNUNET_strdup (""); | 291 | if (GNUNET_OK != |
302 | *optpos = '\0'; | 292 | GNUNET_CONFIGURATION_get_value_string (cfg, "PATHS", |
303 | GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next); | 293 | optpos + 1, &val)) |
304 | GNUNET_free (options); | 294 | val = GNUNET_strdup (""); |
305 | GNUNET_free (val); | 295 | *optpos = '\0'; |
306 | options = optpos; | 296 | GNUNET_asprintf (&optpos, "%s%s%c%s", options, val, b, next); |
307 | } | 297 | GNUNET_free (options); |
298 | GNUNET_free (val); | ||
299 | options = optpos; | ||
300 | } | ||
301 | } | ||
308 | } | 302 | } |
309 | } | ||
310 | use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG"); | 303 | use_debug = GNUNET_CONFIGURATION_get_value_yesno (cfg, sl->name, "DEBUG"); |
311 | 304 | ||
305 | /* actually start process */ | ||
312 | #if DEBUG_ARM | 306 | #if DEBUG_ARM |
313 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | 307 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
314 | "Starting service `%s' using binary `%s' and configuration `%s'\n", | 308 | "Starting service `%s' using binary `%s' and configuration `%s'\n", |
315 | sl->name, sl->binary, sl->config); | 309 | sl->name, sl->binary, sl->config); |
316 | #endif | 310 | #endif |
317 | if (GNUNET_YES == use_debug) | 311 | if (GNUNET_YES == use_debug) |
318 | sl->proc = | 312 | sl->proc = |
319 | do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L", | 313 | do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, "-L", |
320 | "DEBUG", options, NULL); | 314 | "DEBUG", options, NULL); |
321 | else | 315 | else |
322 | sl->proc = | 316 | sl->proc = |
323 | do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, | 317 | do_start_process (lsocks, loprefix, sl->binary, "-c", sl->config, |
324 | options, NULL); | 318 | options, NULL); |
325 | if (sl->proc == NULL) | 319 | if (sl->proc == NULL) |
326 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"), | 320 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, _("Failed to start service `%s'\n"), |
327 | sl->name); | 321 | sl->name); |
328 | else | 322 | else |
329 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), sl->name); | 323 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Starting service `%s'\n"), |
324 | sl->name); | ||
325 | /* clean up */ | ||
330 | GNUNET_free (loprefix); | 326 | GNUNET_free (loprefix); |
331 | GNUNET_free (options); | 327 | GNUNET_free (options); |
332 | } | 328 | GNUNET_array_grow (lsocks, ls, 0); |
333 | |||
334 | |||
335 | /** | ||
336 | * Put the default services represented by a space separated string into an array of strings | ||
337 | * | ||
338 | * @param services space separated string of default services | ||
339 | */ | ||
340 | static void | ||
341 | addDefaultServicesToList (const char *services) | ||
342 | { | ||
343 | unsigned int i; | ||
344 | const char *token; | ||
345 | char *s; | ||
346 | |||
347 | if (strlen (services) == 0) | ||
348 | return; | ||
349 | s = GNUNET_strdup (services); | ||
350 | token = strtok (s, " "); | ||
351 | while (NULL != token) | ||
352 | { | ||
353 | numDefaultServices++; | ||
354 | token = strtok (NULL, " "); | ||
355 | } | ||
356 | GNUNET_free (s); | ||
357 | |||
358 | defaultServicesList = GNUNET_malloc (numDefaultServices * sizeof (char *)); | ||
359 | i = 0; | ||
360 | s = GNUNET_strdup (services); | ||
361 | token = strtok (s, " "); | ||
362 | while (NULL != token) | ||
363 | { | ||
364 | defaultServicesList[i++] = GNUNET_strdup (token); | ||
365 | token = strtok (NULL, " "); | ||
366 | } | ||
367 | GNUNET_free (s); | ||
368 | GNUNET_assert (i == numDefaultServices); | ||
369 | } | ||
370 | |||
371 | |||
372 | /** | ||
373 | * Checks whether the serviceName is in the list of default services | ||
374 | * | ||
375 | * @param serviceName string to check its existance in the list | ||
376 | * @return GNUNET_YES if the service is started by default | ||
377 | */ | ||
378 | static int | ||
379 | isInDefaultList (const char *serviceName) | ||
380 | { | ||
381 | unsigned int i; | ||
382 | |||
383 | for (i = 0; i < numDefaultServices; i++) | ||
384 | if (strcmp (serviceName, defaultServicesList[i]) == 0) | ||
385 | return GNUNET_YES; | ||
386 | return GNUNET_NO; | ||
387 | } | ||
388 | |||
389 | |||
390 | /** | ||
391 | * | ||
392 | */ | ||
393 | static int | ||
394 | stop_listening (const char *serviceName) | ||
395 | { | ||
396 | struct ServiceListeningInfo *pos; | ||
397 | struct ServiceListeningInfo *next; | ||
398 | int ret; | ||
399 | |||
400 | ret = GNUNET_NO; | ||
401 | next = serviceListeningInfoList_head; | ||
402 | while (NULL != (pos = next)) | ||
403 | { | ||
404 | next = pos->next; | ||
405 | if ((serviceName != NULL) && (strcmp (pos->serviceName, serviceName) != 0)) | ||
406 | continue; | ||
407 | if (pos->acceptTask != GNUNET_SCHEDULER_NO_TASK) | ||
408 | GNUNET_SCHEDULER_cancel (pos->acceptTask); | ||
409 | GNUNET_break (GNUNET_OK == | ||
410 | GNUNET_NETWORK_socket_close (pos->listeningSocket)); | ||
411 | GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, | ||
412 | serviceListeningInfoList_tail, pos); | ||
413 | GNUNET_free (pos->serviceName); | ||
414 | GNUNET_free (pos->service_addr); | ||
415 | GNUNET_free (pos); | ||
416 | ret = GNUNET_OK; | ||
417 | } | ||
418 | return ret; | ||
419 | } | 329 | } |
420 | 330 | ||
421 | 331 | ||
@@ -430,25 +340,26 @@ stop_listening (const char *serviceName) | |||
430 | static size_t | 340 | static size_t |
431 | write_result (void *cls, size_t size, void *buf) | 341 | write_result (void *cls, size_t size, void *buf) |
432 | { | 342 | { |
433 | uint16_t *res = cls; | 343 | enum GNUNET_ARM_ProcessStatus *res = cls; |
434 | struct GNUNET_MessageHeader *msg; | 344 | struct GNUNET_ARM_ResultMessage *msg; |
435 | 345 | ||
436 | if (buf == NULL) | 346 | if (buf == NULL) |
437 | { | 347 | { |
438 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | 348 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, |
439 | _("Could not send status result to client\n")); | 349 | _("Could not send status result to client\n")); |
440 | return 0; /* error, not much we can do */ | 350 | return 0; /* error, not much we can do */ |
441 | } | 351 | } |
442 | #if DEBUG_ARM | 352 | #if DEBUG_ARM |
443 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Sending status response %u to client\n", | 353 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, |
444 | (unsigned int) *res); | 354 | "Sending status response %u to client\n", (unsigned int) *res); |
445 | #endif | 355 | #endif |
446 | GNUNET_assert (size >= sizeof (struct GNUNET_MessageHeader)); | 356 | GNUNET_assert (size >= sizeof (struct GNUNET_ARM_ResultMessage)); |
447 | msg = buf; | 357 | msg = buf; |
448 | msg->size = htons (sizeof (struct GNUNET_MessageHeader)); | 358 | msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage)); |
449 | msg->type = htons (*res); | 359 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT); |
360 | msg->status = htonl ((uint32_t) (*res)); | ||
450 | GNUNET_free (res); | 361 | GNUNET_free (res); |
451 | return sizeof (struct GNUNET_MessageHeader); | 362 | return sizeof (struct GNUNET_ARM_ResultMessage); |
452 | } | 363 | } |
453 | 364 | ||
454 | 365 | ||
@@ -463,27 +374,21 @@ write_result (void *cls, size_t size, void *buf) | |||
463 | */ | 374 | */ |
464 | static void | 375 | static void |
465 | signal_result (struct GNUNET_SERVER_Client *client, const char *name, | 376 | signal_result (struct GNUNET_SERVER_Client *client, const char *name, |
466 | uint16_t result) | 377 | enum GNUNET_ARM_ProcessStatus result) |
467 | { | 378 | { |
468 | uint16_t *res; | 379 | enum GNUNET_ARM_ProcessStatus *res; |
469 | 380 | ||
470 | if (NULL == client) | 381 | if (NULL == client) |
471 | { | ||
472 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
473 | _("Not sending status result to client: no client known\n")); | ||
474 | return; | 382 | return; |
475 | } | 383 | /* FIXME: this is not super-clean yet... */ |
476 | #if DEBUG_ARM | 384 | res = GNUNET_malloc (sizeof (enum GNUNET_ARM_ProcessStatus)); |
477 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
478 | "Telling client that service `%s' is now %s\n", name, | ||
479 | result == GNUNET_MESSAGE_TYPE_ARM_IS_DOWN ? "down" : "up"); | ||
480 | #endif | ||
481 | res = GNUNET_malloc (sizeof (uint16_t)); | ||
482 | *res = result; | 385 | *res = result; |
483 | GNUNET_SERVER_notify_transmit_ready (client, | 386 | GNUNET_SERVER_notify_transmit_ready (client, |
484 | sizeof (struct GNUNET_MessageHeader), | 387 | sizeof (struct |
485 | GNUNET_TIME_UNIT_FOREVER_REL, | 388 | GNUNET_ARM_ResultMessage), |
486 | &write_result, res); | 389 | GNUNET_TIME_UNIT_FOREVER_REL, |
390 | &write_result, res); | ||
391 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
487 | } | 392 | } |
488 | 393 | ||
489 | 394 | ||
@@ -497,88 +402,16 @@ signal_result (struct GNUNET_SERVER_Client *client, const char *name, | |||
497 | static struct ServiceList * | 402 | static struct ServiceList * |
498 | find_service (const char *name) | 403 | find_service (const char *name) |
499 | { | 404 | { |
500 | struct ServiceList *pos; | ||
501 | |||
502 | pos = running_head; | ||
503 | while (pos != NULL) | ||
504 | { | ||
505 | if (0 == strcmp (pos->name, name)) | ||
506 | return pos; | ||
507 | pos = pos->next; | ||
508 | } | ||
509 | return NULL; | ||
510 | } | ||
511 | |||
512 | |||
513 | /** | ||
514 | * Start the specified service. | ||
515 | * | ||
516 | * @param client who is asking for this | ||
517 | * @param servicename name of the service to start | ||
518 | * @param lsocks -1 terminated list of listen sockets to pass (systemd style), or NULL | ||
519 | * @return GNUNET_OK on success, GNUNET_SYSERR on error | ||
520 | */ | ||
521 | static int | ||
522 | start_service (struct GNUNET_SERVER_Client *client, const char *servicename, | ||
523 | const SOCKTYPE *lsocks) | ||
524 | { | ||
525 | struct ServiceList *sl; | 405 | struct ServiceList *sl; |
526 | char *binary; | ||
527 | char *config; | ||
528 | struct stat sbuf; | ||
529 | 406 | ||
530 | if (GNUNET_YES == in_shutdown) | 407 | sl = running_head; |
531 | { | 408 | while (sl != NULL) |
532 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 409 | { |
533 | _("ARM is shutting down, service `%s' not started.\n"), | 410 | if (0 == strcmp (sl->name, name)) |
534 | servicename); | 411 | return sl; |
535 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | 412 | sl = sl->next; |
536 | return GNUNET_SYSERR; | 413 | } |
537 | } | 414 | return NULL; |
538 | sl = find_service (servicename); | ||
539 | if (sl != NULL) | ||
540 | { | ||
541 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Service `%s' already running.\n"), | ||
542 | servicename); | ||
543 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); | ||
544 | return GNUNET_SYSERR; | ||
545 | } | ||
546 | if (GNUNET_OK != | ||
547 | GNUNET_CONFIGURATION_get_value_string (cfg, servicename, "BINARY", | ||
548 | &binary)) | ||
549 | { | ||
550 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
551 | _("Binary implementing service `%s' not known!\n"), | ||
552 | servicename); | ||
553 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
554 | return GNUNET_SYSERR; | ||
555 | } | ||
556 | if ((GNUNET_OK != | ||
557 | GNUNET_CONFIGURATION_get_value_filename (cfg, servicename, "CONFIG", | ||
558 | &config)) || | ||
559 | (0 != STAT (config, &sbuf))) | ||
560 | { | ||
561 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
562 | _("Configuration file `%s' for service `%s' not known!\n"), | ||
563 | config, servicename); | ||
564 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
565 | GNUNET_free (binary); | ||
566 | GNUNET_free_non_null (config); | ||
567 | return GNUNET_SYSERR; | ||
568 | } | ||
569 | (void) stop_listening (servicename); | ||
570 | sl = GNUNET_malloc (sizeof (struct ServiceList)); | ||
571 | sl->name = GNUNET_strdup (servicename); | ||
572 | sl->binary = binary; | ||
573 | sl->config = config; | ||
574 | sl->mtime = sbuf.st_mtime; | ||
575 | sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
576 | sl->restartAt = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
577 | GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl); | ||
578 | start_process (sl, lsocks); | ||
579 | if (NULL != client) | ||
580 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UP); | ||
581 | return GNUNET_OK; | ||
582 | } | 415 | } |
583 | 416 | ||
584 | 417 | ||
@@ -590,58 +423,15 @@ start_service (struct GNUNET_SERVER_Client *client, const char *servicename, | |||
590 | * @param tc context | 423 | * @param tc context |
591 | */ | 424 | */ |
592 | static void | 425 | static void |
593 | acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 426 | accept_connection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) |
594 | { | 427 | { |
595 | struct ServiceListeningInfo *sli = cls; | 428 | struct ServiceListeningInfo *sli = cls; |
596 | struct ServiceListeningInfo *pos; | 429 | struct ServiceList *sl = sli->sl; |
597 | struct ServiceListeningInfo *next; | ||
598 | SOCKTYPE *lsocks; | ||
599 | unsigned int ls; | ||
600 | 430 | ||
601 | sli->acceptTask = GNUNET_SCHEDULER_NO_TASK; | 431 | sli->accept_task = GNUNET_SCHEDULER_NO_TASK; |
602 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) | 432 | if (0 != (GNUNET_SCHEDULER_REASON_SHUTDOWN & tc->reason)) |
603 | return; | 433 | return; |
604 | GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, | 434 | start_process (sl); |
605 | serviceListeningInfoList_tail, sli); | ||
606 | lsocks = NULL; | ||
607 | ls = 0; | ||
608 | next = serviceListeningInfoList_head; | ||
609 | while (NULL != (pos = next)) | ||
610 | { | ||
611 | next = pos->next; | ||
612 | if (0 == strcmp (pos->serviceName, sli->serviceName)) | ||
613 | { | ||
614 | GNUNET_array_append (lsocks, ls, | ||
615 | GNUNET_NETWORK_get_fd (pos->listeningSocket)); | ||
616 | GNUNET_free (pos->listeningSocket); /* deliberately no closing! */ | ||
617 | GNUNET_free (pos->service_addr); | ||
618 | GNUNET_free (pos->serviceName); | ||
619 | GNUNET_SCHEDULER_cancel (pos->acceptTask); | ||
620 | GNUNET_CONTAINER_DLL_remove (serviceListeningInfoList_head, | ||
621 | serviceListeningInfoList_tail, pos); | ||
622 | GNUNET_free (pos); | ||
623 | } | ||
624 | } | ||
625 | GNUNET_array_append (lsocks, ls, | ||
626 | GNUNET_NETWORK_get_fd (sli->listeningSocket)); | ||
627 | GNUNET_free (sli->listeningSocket); /* deliberately no closing! */ | ||
628 | GNUNET_free (sli->service_addr); | ||
629 | #if WINDOWS | ||
630 | GNUNET_array_append (lsocks, ls, INVALID_SOCKET); | ||
631 | #else | ||
632 | GNUNET_array_append (lsocks, ls, -1); | ||
633 | #endif | ||
634 | start_service (NULL, sli->serviceName, lsocks); | ||
635 | ls = 0; | ||
636 | while (lsocks[ls] != -1) | ||
637 | #if WINDOWS | ||
638 | GNUNET_break (0 == closesocket (lsocks[ls++])); | ||
639 | #else | ||
640 | GNUNET_break (0 == close (lsocks[ls++])); | ||
641 | #endif | ||
642 | GNUNET_array_grow (lsocks, ls, 0); | ||
643 | GNUNET_free (sli->serviceName); | ||
644 | GNUNET_free (sli); | ||
645 | } | 435 | } |
646 | 436 | ||
647 | 437 | ||
@@ -651,263 +441,104 @@ acceptConnection (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
651 | * | 441 | * |
652 | * @param sa address associated with the service | 442 | * @param sa address associated with the service |
653 | * @param addr_len length of sa | 443 | * @param addr_len length of sa |
654 | * @param serviceName the name of the service in question | 444 | * @param sl service entry for the service in question |
655 | */ | 445 | */ |
656 | static void | 446 | static void |
657 | createListeningSocket (struct sockaddr *sa, socklen_t addr_len, | 447 | create_listen_socket (struct sockaddr *sa, socklen_t addr_len, |
658 | const char *serviceName) | 448 | struct ServiceList *sl) |
659 | { | 449 | { |
660 | const static int on = 1; | 450 | const static int on = 1; |
661 | struct GNUNET_NETWORK_Handle *sock; | 451 | struct GNUNET_NETWORK_Handle *sock; |
662 | struct ServiceListeningInfo *serviceListeningInfo; | 452 | struct ServiceListeningInfo *sli; |
663 | 453 | ||
664 | switch (sa->sa_family) | 454 | switch (sa->sa_family) |
665 | { | 455 | { |
666 | case AF_INET: | 456 | case AF_INET: |
667 | sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); | 457 | sock = GNUNET_NETWORK_socket_create (PF_INET, SOCK_STREAM, 0); |
668 | break; | 458 | break; |
669 | case AF_INET6: | 459 | case AF_INET6: |
670 | sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); | 460 | sock = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); |
671 | break; | 461 | break; |
672 | case AF_UNIX: | 462 | case AF_UNIX: |
673 | if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */ | 463 | if (strcmp (GNUNET_a2s (sa, addr_len), "@") == 0) /* Do not bind to blank UNIX path! */ |
674 | return; | 464 | return; |
675 | sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); | 465 | sock = GNUNET_NETWORK_socket_create (PF_UNIX, SOCK_STREAM, 0); |
676 | break; | 466 | break; |
677 | default: | 467 | default: |
678 | GNUNET_break (0); | 468 | GNUNET_break (0); |
679 | sock = NULL; | 469 | sock = NULL; |
680 | errno = EAFNOSUPPORT; | 470 | errno = EAFNOSUPPORT; |
681 | break; | 471 | break; |
682 | } | 472 | } |
683 | if (NULL == sock) | 473 | if (NULL == sock) |
684 | { | 474 | { |
685 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | 475 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, |
686 | _("Unable to create socket for service `%s': %s\n"), | 476 | _("Unable to create socket for service `%s': %s\n"), |
687 | serviceName, STRERROR (errno)); | 477 | sl->name, STRERROR (errno)); |
688 | GNUNET_free (sa); | 478 | GNUNET_free (sa); |
689 | return; | 479 | return; |
690 | } | 480 | } |
691 | if (GNUNET_NETWORK_socket_setsockopt | 481 | if (GNUNET_NETWORK_socket_setsockopt |
692 | (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) | 482 | (sock, SOL_SOCKET, SO_REUSEADDR, &on, sizeof (on)) != GNUNET_OK) |
693 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | 483 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, |
694 | "setsockopt"); | 484 | "setsockopt"); |
695 | #ifdef IPV6_V6ONLY | 485 | #ifdef IPV6_V6ONLY |
696 | if ((sa->sa_family == AF_INET6) && | 486 | if ((sa->sa_family == AF_INET6) && |
697 | (GNUNET_NETWORK_socket_setsockopt | 487 | (GNUNET_NETWORK_socket_setsockopt |
698 | (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) | 488 | (sock, IPPROTO_IPV6, IPV6_V6ONLY, &on, sizeof (on)) != GNUNET_OK)) |
699 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, | 489 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR | GNUNET_ERROR_TYPE_BULK, |
700 | "setsockopt"); | 490 | "setsockopt"); |
701 | #endif | 491 | #endif |
702 | 492 | ||
703 | if (GNUNET_NETWORK_socket_bind (sock, (const struct sockaddr *) sa, addr_len) | 493 | if (GNUNET_NETWORK_socket_bind |
704 | != GNUNET_OK) | 494 | (sock, (const struct sockaddr *) sa, addr_len) != GNUNET_OK) |
705 | { | 495 | { |
706 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | 496 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, |
707 | _ | 497 | _ |
708 | ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), | 498 | ("Unable to bind listening socket for service `%s' to address `%s': %s\n"), |
709 | serviceName, GNUNET_a2s (sa, addr_len), STRERROR (errno)); | 499 | sl->name, GNUNET_a2s (sa, addr_len), STRERROR (errno)); |
710 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | 500 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); |
711 | GNUNET_free (sa); | 501 | GNUNET_free (sa); |
712 | return; | 502 | return; |
713 | } | 503 | } |
714 | if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK) | 504 | if (GNUNET_NETWORK_socket_listen (sock, 5) != GNUNET_OK) |
715 | { | ||
716 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); | ||
717 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); | ||
718 | GNUNET_free (sa); | ||
719 | return; | ||
720 | } | ||
721 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
722 | _("ARM now monitors connections to service `%s' at `%s'\n"), | ||
723 | serviceName, GNUNET_a2s (sa, addr_len)); | ||
724 | serviceListeningInfo = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); | ||
725 | serviceListeningInfo->serviceName = GNUNET_strdup (serviceName); | ||
726 | serviceListeningInfo->service_addr = sa; | ||
727 | serviceListeningInfo->service_addr_len = addr_len; | ||
728 | serviceListeningInfo->listeningSocket = sock; | ||
729 | serviceListeningInfo->acceptTask = | ||
730 | GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock, | ||
731 | &acceptConnection, serviceListeningInfo); | ||
732 | GNUNET_CONTAINER_DLL_insert (serviceListeningInfoList_head, | ||
733 | serviceListeningInfoList_tail, | ||
734 | serviceListeningInfo); | ||
735 | } | ||
736 | |||
737 | |||
738 | /** | ||
739 | * Callback function, checks whether the current tokens are representing a service, | ||
740 | * gets its addresses and create listening socket for it. | ||
741 | * | ||
742 | * @param cls callback data, not used | ||
743 | * @param section configuration section | ||
744 | * @param option configuration option | ||
745 | * @param value the option's value | ||
746 | */ | ||
747 | static void | ||
748 | checkPortNumberCB (void *cls, const char *section, const char *option, | ||
749 | const char *value) | ||
750 | { | ||
751 | struct sockaddr **addrs; | ||
752 | socklen_t *addr_lens; | ||
753 | int ret; | ||
754 | unsigned int i; | ||
755 | |||
756 | if ((strcasecmp (section, "arm") == 0) || | ||
757 | (strcasecmp (option, "AUTOSTART") != 0) || | ||
758 | (strcasecmp (value, "YES") != 0) || | ||
759 | (isInDefaultList (section) == GNUNET_YES)) | ||
760 | return; | ||
761 | if (0 >= | ||
762 | (ret = | ||
763 | GNUNET_SERVICE_get_server_addresses (section, cfg, &addrs, &addr_lens))) | ||
764 | return; | ||
765 | /* this will free (or capture) addrs[i] */ | ||
766 | for (i = 0; i < ret; i++) | ||
767 | createListeningSocket (addrs[i], addr_lens[i], section); | ||
768 | GNUNET_free (addrs); | ||
769 | GNUNET_free (addr_lens); | ||
770 | } | ||
771 | |||
772 | |||
773 | /** | ||
774 | * Entry point to the Service Manager | ||
775 | * | ||
776 | * @param configurationHandle configuration to use to get services | ||
777 | */ | ||
778 | static void | ||
779 | prepare_services (const struct GNUNET_CONFIGURATION_Handle *configurationHandle) | ||
780 | { | ||
781 | char *defaultServicesString; | ||
782 | |||
783 | cfg = configurationHandle; | ||
784 | /* Split the default services into a list */ | ||
785 | if (GNUNET_OK == | ||
786 | GNUNET_CONFIGURATION_get_value_string (cfg, "arm", "DEFAULTSERVICES", | ||
787 | &defaultServicesString)) | ||
788 | { | ||
789 | addDefaultServicesToList (defaultServicesString); | ||
790 | GNUNET_free (defaultServicesString); | ||
791 | } | ||
792 | /* Spot the services from the configuration and create a listening | ||
793 | * socket for each */ | ||
794 | GNUNET_CONFIGURATION_iterate (cfg, &checkPortNumberCB, NULL); | ||
795 | } | ||
796 | |||
797 | |||
798 | |||
799 | /** | ||
800 | * If the configuration file changes, restart tasks that depended on that | ||
801 | * option. | ||
802 | * | ||
803 | * @param cls closure, NULL if we need to self-restart | ||
804 | * @param tc context | ||
805 | */ | ||
806 | static void | ||
807 | config_change_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
808 | { | ||
809 | struct ServiceList *pos; | ||
810 | struct stat sbuf; | ||
811 | |||
812 | pos = running_head; | ||
813 | while (pos != NULL) | ||
814 | { | ||
815 | /* FIXME: this test for config change may be a bit too coarse grained */ | ||
816 | if ((0 == STAT (pos->config, &sbuf)) && (pos->mtime < sbuf.st_mtime) && | ||
817 | (pos->proc != NULL)) | ||
818 | { | 505 | { |
819 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | 506 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, "listen"); |
820 | _ | 507 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (sock)); |
821 | ("Restarting service `%s' due to configuration file change.\n")); | 508 | GNUNET_free (sa); |
822 | if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) | 509 | return; |
823 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
824 | else | ||
825 | pos->backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
826 | } | 510 | } |
827 | pos = pos->next; | 511 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
828 | } | 512 | _("ARM now monitors connections to service `%s' at `%s'\n"), |
829 | } | 513 | sl->name, GNUNET_a2s (sa, addr_len)); |
830 | 514 | sli = GNUNET_malloc (sizeof (struct ServiceListeningInfo)); | |
831 | 515 | sli->service_addr = sa; | |
832 | /** | 516 | sli->service_addr_len = addr_len; |
833 | * Remove and free an entry in the service list. | 517 | sli->listen_socket = sock; |
834 | * | 518 | sli->sl = sl; |
835 | * @param pos entry to free | 519 | sli->accept_task = |
836 | */ | 520 | GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, sock, |
837 | static void | 521 | &accept_connection, sli); |
838 | free_service (struct ServiceList *pos) | 522 | GNUNET_CONTAINER_DLL_insert (sl->listen_head, sl->listen_tail, sli); |
839 | { | ||
840 | GNUNET_CONTAINER_DLL_remove (running_head, running_tail, pos); | ||
841 | GNUNET_free_non_null (pos->config); | ||
842 | GNUNET_free_non_null (pos->binary); | ||
843 | GNUNET_free (pos->name); | ||
844 | GNUNET_free (pos); | ||
845 | } | 523 | } |
846 | 524 | ||
847 | 525 | ||
848 | /** | 526 | /** |
849 | * Stop the specified service. | 527 | * Remove and free an entry in the service list. Listen sockets |
528 | * must have already been cleaned up. Only to be called during shutdown. | ||
850 | * | 529 | * |
851 | * @param client who is asking for this | 530 | * @param sl entry to free |
852 | * @param servicename name of the service to stop | ||
853 | */ | 531 | */ |
854 | static void | 532 | static void |
855 | stop_service (struct GNUNET_SERVER_Client *client, const char *servicename) | 533 | free_service (struct ServiceList *sl) |
856 | { | 534 | { |
857 | struct ServiceList *pos; | 535 | GNUNET_assert (GNUNET_YES == in_shutdown); |
858 | 536 | GNUNET_CONTAINER_DLL_remove (running_head, running_tail, sl); | |
859 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Preparing to stop `%s'\n"), | 537 | GNUNET_assert (NULL == sl->listen_head); |
860 | servicename); | 538 | GNUNET_free_non_null (sl->config); |
861 | pos = find_service (servicename); | 539 | GNUNET_free_non_null (sl->binary); |
862 | if (pos == NULL) | 540 | GNUNET_free (sl->name); |
863 | { | 541 | GNUNET_free (sl); |
864 | if (GNUNET_OK == stop_listening (servicename)) | ||
865 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
866 | else | ||
867 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_UNKNOWN); | ||
868 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
869 | return; | ||
870 | } | ||
871 | if (pos->killing_client != NULL) | ||
872 | { | ||
873 | /* killing already in progress */ | ||
874 | #if DEBUG_ARM | ||
875 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' is already down\n", | ||
876 | servicename); | ||
877 | #endif | ||
878 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
879 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
880 | return; | ||
881 | } | ||
882 | |||
883 | if (GNUNET_YES == in_shutdown) | ||
884 | { | ||
885 | #if DEBUG_ARM | ||
886 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
887 | "Termination request already sent to `%s' (since ARM is in shutdown).\n", | ||
888 | servicename); | ||
889 | #endif | ||
890 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
891 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
892 | return; | ||
893 | } | ||
894 | if (pos->proc == NULL) | ||
895 | { | ||
896 | /* process is in delayed restart, simply remove it! */ | ||
897 | free_service (pos); | ||
898 | signal_result (client, servicename, GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
899 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
900 | return; | ||
901 | } | ||
902 | #if DEBUG_ARM | ||
903 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
904 | "Sending kill signal to service `%s', waiting for process to die.\n", | ||
905 | servicename); | ||
906 | #endif | ||
907 | if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) | ||
908 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
909 | pos->killing_client = client; | ||
910 | GNUNET_SERVER_client_keep (client); | ||
911 | } | 542 | } |
912 | 543 | ||
913 | 544 | ||
@@ -922,22 +553,40 @@ stop_service (struct GNUNET_SERVER_Client *client, const char *servicename) | |||
922 | */ | 553 | */ |
923 | static void | 554 | static void |
924 | handle_start (void *cls, struct GNUNET_SERVER_Client *client, | 555 | handle_start (void *cls, struct GNUNET_SERVER_Client *client, |
925 | const struct GNUNET_MessageHeader *message) | 556 | const struct GNUNET_MessageHeader *message) |
926 | { | 557 | { |
927 | const char *servicename; | 558 | const char *servicename; |
559 | struct ServiceList *sl; | ||
928 | uint16_t size; | 560 | uint16_t size; |
929 | 561 | ||
930 | size = ntohs (message->size); | 562 | size = ntohs (message->size); |
931 | size -= sizeof (struct GNUNET_MessageHeader); | 563 | size -= sizeof (struct GNUNET_MessageHeader); |
932 | servicename = (const char *) &message[1]; | 564 | servicename = (const char *) &message[1]; |
933 | if ((size == 0) || (servicename[size - 1] != '\0')) | 565 | if ((size == 0) || (servicename[size - 1] != '\0')) |
934 | { | 566 | { |
935 | GNUNET_break (0); | 567 | GNUNET_break (0); |
936 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | 568 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); |
937 | return; | 569 | return; |
938 | } | 570 | } |
939 | start_service (client, servicename, NULL); | 571 | if (GNUNET_YES == in_shutdown) |
940 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | 572 | { |
573 | signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); | ||
574 | return; | ||
575 | } | ||
576 | sl = find_service (servicename); | ||
577 | if (NULL == sl) | ||
578 | { | ||
579 | signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); | ||
580 | return; | ||
581 | } | ||
582 | sl->is_default = GNUNET_YES; | ||
583 | if (sl->proc != NULL) | ||
584 | { | ||
585 | signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_RUNNING); | ||
586 | return; | ||
587 | } | ||
588 | start_process (sl); | ||
589 | signal_result (client, servicename, GNUNET_ARM_PROCESS_STARTING); | ||
941 | } | 590 | } |
942 | 591 | ||
943 | 592 | ||
@@ -952,8 +601,9 @@ handle_start (void *cls, struct GNUNET_SERVER_Client *client, | |||
952 | */ | 601 | */ |
953 | static void | 602 | static void |
954 | handle_stop (void *cls, struct GNUNET_SERVER_Client *client, | 603 | handle_stop (void *cls, struct GNUNET_SERVER_Client *client, |
955 | const struct GNUNET_MessageHeader *message) | 604 | const struct GNUNET_MessageHeader *message) |
956 | { | 605 | { |
606 | struct ServiceList *sl; | ||
957 | const char *servicename; | 607 | const char *servicename; |
958 | uint16_t size; | 608 | uint16_t size; |
959 | 609 | ||
@@ -961,33 +611,48 @@ handle_stop (void *cls, struct GNUNET_SERVER_Client *client, | |||
961 | size -= sizeof (struct GNUNET_MessageHeader); | 611 | size -= sizeof (struct GNUNET_MessageHeader); |
962 | servicename = (const char *) &message[1]; | 612 | servicename = (const char *) &message[1]; |
963 | if ((size == 0) || (servicename[size - 1] != '\0')) | 613 | if ((size == 0) || (servicename[size - 1] != '\0')) |
964 | { | 614 | { |
965 | GNUNET_break (0); | 615 | GNUNET_break (0); |
966 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | 616 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); |
967 | return; | 617 | return; |
968 | } | 618 | } |
969 | stop_service (client, servicename); | 619 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
970 | } | 620 | _("Preparing to stop `%s'\n"), servicename); |
971 | 621 | sl = find_service (servicename); | |
972 | 622 | if (sl == NULL) | |
973 | /** | 623 | { |
974 | * Remove all entries for tasks that are not running | 624 | signal_result (client, servicename, GNUNET_ARM_PROCESS_UNKNOWN); |
975 | * (proc = NULL) from the running list (they will no longer | 625 | return; |
976 | * be restarted since we are shutting down). | 626 | } |
977 | */ | 627 | sl->is_default = GNUNET_NO; |
978 | static void | 628 | if (GNUNET_YES == in_shutdown) |
979 | clean_up_running () | 629 | { |
980 | { | 630 | /* shutdown in progress */ |
981 | struct ServiceList *pos; | 631 | signal_result (client, servicename, GNUNET_ARM_PROCESS_SHUTDOWN); |
982 | struct ServiceList *next; | 632 | return; |
983 | 633 | } | |
984 | next = running_head; | 634 | if (sl->killing_client != NULL) |
985 | while (NULL != (pos = next)) | 635 | { |
986 | { | 636 | /* killing already in progress */ |
987 | next = pos->next; | 637 | signal_result (client, servicename, |
988 | if (pos->proc == NULL) | 638 | GNUNET_ARM_PROCESS_ALREADY_STOPPING); |
989 | free_service (pos); | 639 | return; |
990 | } | 640 | } |
641 | if (sl->proc == NULL) | ||
642 | { | ||
643 | /* process is down */ | ||
644 | signal_result (client, servicename, GNUNET_ARM_PROCESS_ALREADY_DOWN); | ||
645 | return; | ||
646 | } | ||
647 | #if DEBUG_ARM | ||
648 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
649 | "Sending kill signal to service `%s', waiting for process to die.\n", | ||
650 | servicename); | ||
651 | #endif | ||
652 | if (0 != GNUNET_OS_process_kill (sl->proc, SIGTERM)) | ||
653 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
654 | sl->killing_client = client; | ||
655 | GNUNET_SERVER_client_keep (client); | ||
991 | } | 656 | } |
992 | 657 | ||
993 | 658 | ||
@@ -999,15 +664,15 @@ static void | |||
999 | do_shutdown () | 664 | do_shutdown () |
1000 | { | 665 | { |
1001 | if (NULL != server) | 666 | if (NULL != server) |
1002 | { | 667 | { |
1003 | GNUNET_SERVER_destroy (server); | 668 | GNUNET_SERVER_destroy (server); |
1004 | server = NULL; | 669 | server = NULL; |
1005 | } | 670 | } |
1006 | if (GNUNET_SCHEDULER_NO_TASK != child_death_task) | 671 | if (GNUNET_SCHEDULER_NO_TASK != child_death_task) |
1007 | { | 672 | { |
1008 | GNUNET_SCHEDULER_cancel (child_death_task); | 673 | GNUNET_SCHEDULER_cancel (child_death_task); |
1009 | child_death_task = GNUNET_SCHEDULER_NO_TASK; | 674 | child_death_task = GNUNET_SCHEDULER_NO_TASK; |
1010 | } | 675 | } |
1011 | } | 676 | } |
1012 | 677 | ||
1013 | 678 | ||
@@ -1022,33 +687,50 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1022 | { | 687 | { |
1023 | struct ServiceList *pos; | 688 | struct ServiceList *pos; |
1024 | struct ServiceList *nxt; | 689 | struct ServiceList *nxt; |
690 | struct ServiceListeningInfo *sli; | ||
1025 | 691 | ||
1026 | #if DEBUG_ARM | ||
1027 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, _("Stopping all services\n")); | ||
1028 | #endif | ||
1029 | if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) | 692 | if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) |
1030 | { | 693 | { |
1031 | GNUNET_SCHEDULER_cancel (child_restart_task); | 694 | GNUNET_SCHEDULER_cancel (child_restart_task); |
1032 | child_restart_task = GNUNET_SCHEDULER_NO_TASK; | 695 | child_restart_task = GNUNET_SCHEDULER_NO_TASK; |
1033 | } | 696 | } |
1034 | in_shutdown = GNUNET_YES; | 697 | in_shutdown = GNUNET_YES; |
1035 | stop_listening (NULL); | 698 | /* first, stop listening */ |
1036 | pos = running_head; | 699 | for (pos = running_head; NULL != pos; pos = pos->next) |
1037 | while (NULL != pos) | ||
1038 | { | ||
1039 | nxt = pos->next; | ||
1040 | if (pos->proc != NULL) | ||
1041 | { | 700 | { |
1042 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", pos->name); | 701 | while (NULL != (sli = pos->listen_head)) |
1043 | if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) | 702 | { |
1044 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | 703 | GNUNET_CONTAINER_DLL_remove (pos->listen_head, |
704 | pos->listen_tail, sli); | ||
705 | if (sli->accept_task != GNUNET_SCHEDULER_NO_TASK) | ||
706 | { | ||
707 | GNUNET_SCHEDULER_cancel (sli->accept_task); | ||
708 | sli->accept_task = GNUNET_SCHEDULER_NO_TASK; | ||
709 | } | ||
710 | GNUNET_break (GNUNET_OK == | ||
711 | GNUNET_NETWORK_socket_close (sli->listen_socket)); | ||
712 | GNUNET_free (sli->service_addr); | ||
713 | GNUNET_free (sli); | ||
714 | } | ||
1045 | } | 715 | } |
1046 | else | 716 | /* then, shutdown all existing service processes */ |
717 | nxt = running_head; | ||
718 | while (NULL != (pos = nxt)) | ||
1047 | { | 719 | { |
1048 | free_service (pos); | 720 | nxt = pos->next; |
721 | if (pos->proc != NULL) | ||
722 | { | ||
723 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Stopping service `%s'\n", | ||
724 | pos->name); | ||
725 | if (0 != GNUNET_OS_process_kill (pos->proc, SIGTERM)) | ||
726 | GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, "kill"); | ||
727 | } | ||
728 | else | ||
729 | { | ||
730 | free_service (pos); | ||
731 | } | ||
1049 | } | 732 | } |
1050 | pos = nxt; | 733 | /* finally, should all service processes be already gone, terminate for real */ |
1051 | } | ||
1052 | if (running_head == NULL) | 734 | if (running_head == NULL) |
1053 | do_shutdown (); | 735 | do_shutdown (); |
1054 | } | 736 | } |
@@ -1061,10 +743,12 @@ shutdown_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1061 | * @param tc context | 743 | * @param tc context |
1062 | */ | 744 | */ |
1063 | static void | 745 | static void |
1064 | delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | 746 | delayed_restart_task (void *cls, |
747 | const struct GNUNET_SCHEDULER_TaskContext *tc) | ||
1065 | { | 748 | { |
1066 | struct ServiceList *pos; | 749 | struct ServiceList *sl; |
1067 | struct GNUNET_TIME_Relative lowestRestartDelay; | 750 | struct GNUNET_TIME_Relative lowestRestartDelay; |
751 | struct ServiceListeningInfo *sli; | ||
1068 | 752 | ||
1069 | child_restart_task = GNUNET_SCHEDULER_NO_TASK; | 753 | child_restart_task = GNUNET_SCHEDULER_NO_TASK; |
1070 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | 754 | if (0 != (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) |
@@ -1074,37 +758,56 @@ delayed_restart_task (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1074 | 758 | ||
1075 | /* check for services that need to be restarted due to | 759 | /* check for services that need to be restarted due to |
1076 | * configuration changes or because the last restart failed */ | 760 | * configuration changes or because the last restart failed */ |
1077 | pos = running_head; | 761 | for (sl = running_head; NULL != sl; sl = sl->next) |
1078 | while (pos != NULL) | ||
1079 | { | ||
1080 | if (pos->proc == NULL) | ||
1081 | { | 762 | { |
1082 | if (GNUNET_TIME_absolute_get_remaining (pos->restartAt).rel_value == 0) | 763 | if (sl->proc == NULL) |
1083 | { | 764 | { |
1084 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Restarting service `%s'.\n"), | 765 | /* service is currently not running */ |
1085 | pos->name); | 766 | if (GNUNET_TIME_absolute_get_remaining (sl->restart_at).rel_value == |
1086 | start_process (pos, NULL); | 767 | 0) |
1087 | } | 768 | { |
1088 | else | 769 | /* restart is now allowed */ |
1089 | { | 770 | if (sl->is_default) |
1090 | lowestRestartDelay = | 771 | { |
1091 | GNUNET_TIME_relative_min (lowestRestartDelay, | 772 | /* process should run by default, start immediately */ |
1092 | GNUNET_TIME_absolute_get_remaining | 773 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
1093 | (pos->restartAt)); | 774 | _("Restarting service `%s'.\n"), sl->name); |
1094 | } | 775 | start_process (sl); |
776 | } | ||
777 | else | ||
778 | { | ||
779 | /* process is run on-demand, ensure it is re-started if there is demand */ | ||
780 | for (sli = sl->listen_head; NULL != sli; sli = sli->next) | ||
781 | if (GNUNET_SCHEDULER_NO_TASK == sli->accept_task) | ||
782 | { | ||
783 | /* accept was actually paused, so start it again */ | ||
784 | sli->accept_task = | ||
785 | GNUNET_SCHEDULER_add_read_net | ||
786 | (GNUNET_TIME_UNIT_FOREVER_REL, sli->listen_socket, | ||
787 | &accept_connection, sli); | ||
788 | } | ||
789 | } | ||
790 | } | ||
791 | else | ||
792 | { | ||
793 | /* update calculation for earliest time to reactivate a service */ | ||
794 | lowestRestartDelay = | ||
795 | GNUNET_TIME_relative_min (lowestRestartDelay, | ||
796 | GNUNET_TIME_absolute_get_remaining | ||
797 | (sl->restart_at)); | ||
798 | } | ||
799 | } | ||
1095 | } | 800 | } |
1096 | pos = pos->next; | ||
1097 | } | ||
1098 | if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value) | 801 | if (lowestRestartDelay.rel_value != GNUNET_TIME_UNIT_FOREVER_REL.rel_value) |
1099 | { | 802 | { |
1100 | #if DEBUG_ARM | 803 | #if DEBUG_ARM |
1101 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n", | 804 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Will restart process in %llums\n", |
1102 | (unsigned long long) lowestRestartDelay.rel_value); | 805 | (unsigned long long) lowestRestartDelay.rel_value); |
1103 | #endif | 806 | #endif |
1104 | child_restart_task = | 807 | child_restart_task = |
1105 | GNUNET_SCHEDULER_add_delayed (lowestRestartDelay, &delayed_restart_task, | 808 | GNUNET_SCHEDULER_add_delayed (lowestRestartDelay, |
1106 | NULL); | 809 | &delayed_restart_task, NULL); |
1107 | } | 810 | } |
1108 | } | 811 | } |
1109 | 812 | ||
1110 | 813 | ||
@@ -1120,126 +823,150 @@ maint_child_death (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc) | |||
1120 | { | 823 | { |
1121 | struct ServiceList *pos; | 824 | struct ServiceList *pos; |
1122 | struct ServiceList *next; | 825 | struct ServiceList *next; |
826 | struct ServiceListeningInfo *sli; | ||
1123 | const char *statstr; | 827 | const char *statstr; |
1124 | int statcode; | 828 | int statcode; |
1125 | int ret; | 829 | int ret; |
1126 | char c[16]; | 830 | char c[16]; |
1127 | enum GNUNET_OS_ProcessStatusType statusType; | 831 | enum GNUNET_OS_ProcessStatusType statusType; |
1128 | unsigned long statusCode; | 832 | unsigned long statusCode; |
833 | const struct GNUNET_DISK_FileHandle *pr; | ||
1129 | 834 | ||
835 | pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); | ||
1130 | child_death_task = GNUNET_SCHEDULER_NO_TASK; | 836 | child_death_task = GNUNET_SCHEDULER_NO_TASK; |
1131 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) | 837 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_READ_READY)) |
1132 | { | 838 | { |
1133 | /* shutdown scheduled us, ignore! */ | 839 | /* shutdown scheduled us, ignore! */ |
1134 | child_death_task = | 840 | child_death_task = |
1135 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, | 841 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, |
1136 | &maint_child_death, NULL); | 842 | pr, &maint_child_death, NULL); |
1137 | return; | 843 | return; |
1138 | } | 844 | } |
1139 | /* consume the signal */ | 845 | /* consume the signal */ |
1140 | GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); | 846 | GNUNET_break (0 < GNUNET_DISK_file_read (pr, &c, sizeof (c))); |
1141 | 847 | ||
1142 | /* check for services that died (WAITPID) */ | 848 | /* check for services that died (WAITPID) */ |
1143 | next = running_head; | 849 | next = running_head; |
1144 | while (NULL != (pos = next)) | 850 | while (NULL != (pos = next)) |
1145 | { | ||
1146 | next = pos->next; | ||
1147 | if (pos->proc == NULL) | ||
1148 | continue; | ||
1149 | if ((GNUNET_SYSERR == | ||
1150 | (ret = GNUNET_OS_process_status (pos->proc, &statusType, &statusCode))) | ||
1151 | || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) || | ||
1152 | (statusType == GNUNET_OS_PROCESS_RUNNING))) | ||
1153 | continue; | ||
1154 | |||
1155 | if (statusType == GNUNET_OS_PROCESS_EXITED) | ||
1156 | { | ||
1157 | statstr = _( /* process termination method */ "exit"); | ||
1158 | statcode = statusCode; | ||
1159 | } | ||
1160 | else if (statusType == GNUNET_OS_PROCESS_SIGNALED) | ||
1161 | { | ||
1162 | statstr = _( /* process termination method */ "signal"); | ||
1163 | statcode = statusCode; | ||
1164 | } | ||
1165 | else | ||
1166 | { | ||
1167 | statstr = _( /* process termination method */ "unknown"); | ||
1168 | statcode = 0; | ||
1169 | } | ||
1170 | GNUNET_OS_process_close (pos->proc); | ||
1171 | pos->proc = NULL; | ||
1172 | if (NULL != pos->killing_client) | ||
1173 | { | ||
1174 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Service `%s' stopped\n"), | ||
1175 | pos->name); | ||
1176 | signal_result (pos->killing_client, pos->name, | ||
1177 | GNUNET_MESSAGE_TYPE_ARM_IS_DOWN); | ||
1178 | GNUNET_SERVER_receive_done (pos->killing_client, GNUNET_OK); | ||
1179 | GNUNET_SERVER_client_drop (pos->killing_client); | ||
1180 | free_service (pos); | ||
1181 | continue; | ||
1182 | } | ||
1183 | if (GNUNET_YES != in_shutdown) | ||
1184 | { | 851 | { |
1185 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | 852 | next = pos->next; |
1186 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | 853 | |
1187 | _ | 854 | if (pos->proc == NULL) |
1188 | ("Service `%s' terminated with status %s/%d, will try to restart it!\n"), | 855 | { |
1189 | pos->name, statstr, statcode); | 856 | if (GNUNET_YES == in_shutdown) |
1190 | /* schedule restart */ | 857 | free_service (pos); |
1191 | pos->restartAt = GNUNET_TIME_relative_to_absolute (pos->backoff); | 858 | continue; |
1192 | if (pos->backoff.rel_value < EXPONENTIAL_BACKOFF_THRESHOLD) | 859 | } |
1193 | pos->backoff = GNUNET_TIME_relative_multiply (pos->backoff, 2); | 860 | if ((GNUNET_SYSERR == |
1194 | if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) | 861 | (ret = |
1195 | GNUNET_SCHEDULER_cancel (child_restart_task); | 862 | GNUNET_OS_process_status (pos->proc, &statusType, &statusCode))) |
1196 | child_restart_task = | 863 | || ((ret == GNUNET_NO) || (statusType == GNUNET_OS_PROCESS_STOPPED) |
1197 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, | 864 | || (statusType == GNUNET_OS_PROCESS_RUNNING))) |
1198 | &delayed_restart_task, NULL); | 865 | continue; |
866 | if (statusType == GNUNET_OS_PROCESS_EXITED) | ||
867 | { | ||
868 | statstr = _( /* process termination method */ "exit"); | ||
869 | statcode = statusCode; | ||
870 | } | ||
871 | else if (statusType == GNUNET_OS_PROCESS_SIGNALED) | ||
872 | { | ||
873 | statstr = _( /* process termination method */ "signal"); | ||
874 | statcode = statusCode; | ||
875 | } | ||
876 | else | ||
877 | { | ||
878 | statstr = _( /* process termination method */ "unknown"); | ||
879 | statcode = 0; | ||
880 | } | ||
881 | GNUNET_OS_process_close (pos->proc); | ||
882 | pos->proc = NULL; | ||
883 | if (NULL != pos->killing_client) | ||
884 | { | ||
885 | signal_result (pos->killing_client, pos->name, | ||
886 | GNUNET_ARM_PROCESS_DOWN); | ||
887 | GNUNET_SERVER_client_drop (pos->killing_client); | ||
888 | pos->killing_client = NULL; | ||
889 | /* process can still be re-started on-demand, ensure it is re-started if there is demand */ | ||
890 | for (sli = pos->listen_head; NULL != sli; sli = sli->next) | ||
891 | { | ||
892 | GNUNET_break (GNUNET_SCHEDULER_NO_TASK == sli->accept_task); | ||
893 | sli->accept_task = | ||
894 | GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, | ||
895 | sli->listen_socket, | ||
896 | &accept_connection, sli); | ||
897 | } | ||
898 | continue; | ||
899 | } | ||
900 | if (GNUNET_YES != in_shutdown) | ||
901 | { | ||
902 | if ((statusType == GNUNET_OS_PROCESS_EXITED) && (statcode == 0)) | ||
903 | { | ||
904 | /* process terminated normally, allow restart at any time */ | ||
905 | pos->restart_at.abs_value = 0; | ||
906 | continue; | ||
907 | } | ||
908 | if (0 == (tc->reason & GNUNET_SCHEDULER_REASON_SHUTDOWN)) | ||
909 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
910 | _ | ||
911 | ("Service `%s' terminated with status %s/%d, will restart in %llu ms\n"), | ||
912 | pos->name, statstr, statcode, pos->backoff.rel_value); | ||
913 | /* schedule restart */ | ||
914 | pos->restart_at = GNUNET_TIME_relative_to_absolute (pos->backoff); | ||
915 | pos->backoff = | ||
916 | GNUNET_TIME_relative_min (EXPONENTIAL_BACKOFF_THRESHOLD, | ||
917 | GNUNET_TIME_relative_multiply | ||
918 | (pos->backoff, 2)); | ||
919 | if (GNUNET_SCHEDULER_NO_TASK != child_restart_task) | ||
920 | GNUNET_SCHEDULER_cancel (child_restart_task); | ||
921 | child_restart_task = | ||
922 | GNUNET_SCHEDULER_add_with_priority | ||
923 | (GNUNET_SCHEDULER_PRIORITY_IDLE, &delayed_restart_task, NULL); | ||
924 | } | ||
925 | else | ||
926 | { | ||
927 | free_service (pos); | ||
928 | } | ||
1199 | } | 929 | } |
1200 | #if DEBUG_ARM | ||
1201 | else | ||
1202 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, | ||
1203 | "Service `%s' terminated with status %s/%d\n", pos->name, | ||
1204 | statstr, statcode); | ||
1205 | #endif | ||
1206 | } | ||
1207 | child_death_task = | 930 | child_death_task = |
1208 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, | 931 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, |
1209 | &maint_child_death, NULL); | 932 | pr, &maint_child_death, NULL); |
1210 | if (GNUNET_YES == in_shutdown) | ||
1211 | clean_up_running (); | ||
1212 | if ((NULL == running_head) && (GNUNET_YES == in_shutdown)) | 933 | if ((NULL == running_head) && (GNUNET_YES == in_shutdown)) |
1213 | do_shutdown (); | 934 | do_shutdown (); |
1214 | } | 935 | } |
1215 | 936 | ||
1216 | 937 | ||
938 | /** | ||
939 | * Transmit our shutdown acknowledgement to the client. | ||
940 | * | ||
941 | * @param cls the 'struct GNUNET_SERVER_Client' | ||
942 | * @param size number of bytes available in buf | ||
943 | * @param buf where to write the message | ||
944 | * @return number of bytes written | ||
945 | */ | ||
1217 | static size_t | 946 | static size_t |
1218 | transmit_shutdown_ack (void *cls, size_t size, void *buf) | 947 | transmit_shutdown_ack (void *cls, size_t size, void *buf) |
1219 | { | 948 | { |
1220 | struct GNUNET_SERVER_Client *client = cls; | 949 | struct GNUNET_SERVER_Client *client = cls; |
1221 | struct GNUNET_MessageHeader *msg; | 950 | struct GNUNET_ARM_ResultMessage *msg; |
1222 | |||
1223 | if (size < sizeof (struct GNUNET_MessageHeader)) | ||
1224 | { | ||
1225 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1226 | _("Failed to transmit shutdown ACK.\n")); | ||
1227 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
1228 | return 0; /* client disconnected */ | ||
1229 | } | ||
1230 | |||
1231 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, _("Transmitting shutdown ACK.\n")); | ||
1232 | 951 | ||
952 | if (size < sizeof (struct GNUNET_ARM_ResultMessage)) | ||
953 | { | ||
954 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
955 | _("Failed to transmit shutdown ACK.\n")); | ||
956 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
957 | return 0; /* client disconnected */ | ||
958 | } | ||
1233 | /* Make the connection flushing for the purpose of ACK transmitting, | 959 | /* Make the connection flushing for the purpose of ACK transmitting, |
1234 | * needed on W32 to ensure that the message is even received, harmless | 960 | * needed on W32 to ensure that the message is even received, harmless |
1235 | * on other platforms... */ | 961 | * on other platforms... */ |
1236 | GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client)); | 962 | GNUNET_break (GNUNET_OK == GNUNET_SERVER_client_disable_corking (client)); |
1237 | msg = (struct GNUNET_MessageHeader *) buf; | 963 | msg = (struct GNUNET_ARM_ResultMessage *) buf; |
1238 | msg->type = htons (GNUNET_MESSAGE_TYPE_ARM_SHUTDOWN_ACK); | 964 | msg->header.type = htons (GNUNET_MESSAGE_TYPE_ARM_RESULT); |
1239 | msg->size = htons (sizeof (struct GNUNET_MessageHeader)); | 965 | msg->header.size = htons (sizeof (struct GNUNET_ARM_ResultMessage)); |
966 | msg->status = htonl ((uint32_t) GNUNET_ARM_PROCESS_SHUTDOWN); | ||
1240 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | 967 | GNUNET_SERVER_receive_done (client, GNUNET_OK); |
1241 | GNUNET_SERVER_client_drop (client); | 968 | GNUNET_SERVER_client_drop (client); |
1242 | return sizeof (struct GNUNET_MessageHeader); | 969 | return sizeof (struct GNUNET_ARM_ResultMessage); |
1243 | } | 970 | } |
1244 | 971 | ||
1245 | 972 | ||
@@ -1252,17 +979,15 @@ transmit_shutdown_ack (void *cls, size_t size, void *buf) | |||
1252 | */ | 979 | */ |
1253 | static void | 980 | static void |
1254 | handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client, | 981 | handle_shutdown (void *cls, struct GNUNET_SERVER_Client *client, |
1255 | const struct GNUNET_MessageHeader *message) | 982 | const struct GNUNET_MessageHeader *message) |
1256 | { | 983 | { |
984 | GNUNET_SCHEDULER_shutdown (); | ||
1257 | GNUNET_SERVER_client_keep (client); | 985 | GNUNET_SERVER_client_keep (client); |
1258 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, | ||
1259 | _("Initiating shutdown as requested by client.\n")); | ||
1260 | GNUNET_SERVER_notify_transmit_ready (client, | 986 | GNUNET_SERVER_notify_transmit_ready (client, |
1261 | sizeof (struct GNUNET_MessageHeader), | 987 | sizeof (struct GNUNET_ARM_ResultMessage), |
1262 | GNUNET_TIME_UNIT_FOREVER_REL, | 988 | GNUNET_TIME_UNIT_FOREVER_REL, |
1263 | &transmit_shutdown_ack, client); | 989 | &transmit_shutdown_ack, client); |
1264 | GNUNET_SERVER_client_persist_ (client); | 990 | GNUNET_SERVER_client_persist_ (client); |
1265 | GNUNET_SCHEDULER_shutdown (); | ||
1266 | } | 991 | } |
1267 | 992 | ||
1268 | 993 | ||
@@ -1274,13 +999,77 @@ static void | |||
1274 | sighandler_child_death () | 999 | sighandler_child_death () |
1275 | { | 1000 | { |
1276 | static char c; | 1001 | static char c; |
1277 | int old_errno = errno; /* back-up errno */ | 1002 | int old_errno = errno; /* back-up errno */ |
1278 | 1003 | ||
1279 | GNUNET_break (1 == | 1004 | GNUNET_break (1 == |
1280 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle | 1005 | GNUNET_DISK_file_write (GNUNET_DISK_pipe_handle |
1281 | (sigpipe, GNUNET_DISK_PIPE_END_WRITE), | 1006 | (sigpipe, GNUNET_DISK_PIPE_END_WRITE), |
1282 | &c, sizeof (c))); | 1007 | &c, sizeof (c))); |
1283 | errno = old_errno; /* restore errno */ | 1008 | errno = old_errno; /* restore errno */ |
1009 | } | ||
1010 | |||
1011 | |||
1012 | /** | ||
1013 | * Setup our service record for the given section in the configuration file | ||
1014 | * (assuming the section is for a service). | ||
1015 | * | ||
1016 | * @param cls unused | ||
1017 | * @param section a section in the configuration file | ||
1018 | * @return GNUNET_OK (continue) | ||
1019 | */ | ||
1020 | static void | ||
1021 | setup_service (void *cls, const char *section) | ||
1022 | { | ||
1023 | struct ServiceList *sl; | ||
1024 | char *binary; | ||
1025 | char *config; | ||
1026 | struct stat sbuf; | ||
1027 | struct sockaddr **addrs; | ||
1028 | socklen_t *addr_lens; | ||
1029 | int ret; | ||
1030 | unsigned int i; | ||
1031 | |||
1032 | if (strcasecmp (section, "arm") == 0) | ||
1033 | return; | ||
1034 | if (GNUNET_OK != | ||
1035 | GNUNET_CONFIGURATION_get_value_string (cfg, section, "BINARY", &binary)) | ||
1036 | { | ||
1037 | /* not a service section */ | ||
1038 | return; | ||
1039 | } | ||
1040 | config = NULL; | ||
1041 | if ((GNUNET_OK != | ||
1042 | GNUNET_CONFIGURATION_get_value_filename (cfg, section, "CONFIG", | ||
1043 | &config)) || | ||
1044 | (0 != STAT (config, &sbuf))) | ||
1045 | { | ||
1046 | GNUNET_log (GNUNET_ERROR_TYPE_WARNING, | ||
1047 | _ | ||
1048 | ("Configuration file `%s' for service `%s' not valid: %s\n"), | ||
1049 | config, section, | ||
1050 | (config == NULL) ? _("option missing") : STRERROR (errno)); | ||
1051 | GNUNET_free (binary); | ||
1052 | GNUNET_free_non_null (config); | ||
1053 | return; | ||
1054 | } | ||
1055 | sl = GNUNET_malloc (sizeof (struct ServiceList)); | ||
1056 | sl->name = GNUNET_strdup (section); | ||
1057 | sl->binary = binary; | ||
1058 | sl->config = config; | ||
1059 | sl->backoff = GNUNET_TIME_UNIT_MILLISECONDS; | ||
1060 | sl->restart_at = GNUNET_TIME_UNIT_FOREVER_ABS; | ||
1061 | GNUNET_CONTAINER_DLL_insert (running_head, running_tail, sl); | ||
1062 | if (GNUNET_YES != | ||
1063 | GNUNET_CONFIGURATION_get_value_yesno (cfg, section, "AUTOSTART")) | ||
1064 | return; | ||
1065 | if (0 >= (ret = GNUNET_SERVICE_get_server_addresses (section, cfg, | ||
1066 | &addrs, &addr_lens))) | ||
1067 | return; | ||
1068 | /* this will free (or capture) addrs[i] */ | ||
1069 | for (i = 0; i < ret; i++) | ||
1070 | create_listen_socket (addrs[i], addr_lens[i], sl); | ||
1071 | GNUNET_free (addrs); | ||
1072 | GNUNET_free (addr_lens); | ||
1284 | } | 1073 | } |
1285 | 1074 | ||
1286 | 1075 | ||
@@ -1303,64 +1092,68 @@ run (void *cls, struct GNUNET_SERVER_Handle *serv, | |||
1303 | {NULL, NULL, 0, 0} | 1092 | {NULL, NULL, 0, 0} |
1304 | }; | 1093 | }; |
1305 | char *defaultservices; | 1094 | char *defaultservices; |
1306 | char *pos; | 1095 | const char *pos; |
1096 | struct ServiceList *sl; | ||
1307 | 1097 | ||
1308 | cfg = c; | 1098 | cfg = c; |
1309 | server = serv; | 1099 | server = serv; |
1310 | GNUNET_assert (serv != NULL); | 1100 | GNUNET_assert (serv != NULL); |
1311 | pr = GNUNET_DISK_pipe_handle (sigpipe, GNUNET_DISK_PIPE_END_READ); | ||
1312 | GNUNET_assert (pr != NULL); | ||
1313 | GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES); | 1101 | GNUNET_SERVER_ignore_shutdown (serv, GNUNET_YES); |
1314 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, | 1102 | GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL, &shutdown_task, |
1315 | NULL); | 1103 | NULL); |
1316 | child_death_task = | 1104 | child_death_task = |
1317 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, pr, | 1105 | GNUNET_SCHEDULER_add_read_file (GNUNET_TIME_UNIT_FOREVER_REL, |
1318 | &maint_child_death, NULL); | 1106 | GNUNET_DISK_pipe_handle (sigpipe, |
1107 | GNUNET_DISK_PIPE_END_READ), | ||
1108 | &maint_child_death, NULL); | ||
1319 | 1109 | ||
1320 | if (GNUNET_OK != | 1110 | if (GNUNET_OK != |
1321 | GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX", | 1111 | GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_PREFIX", |
1322 | &prefix_command)) | 1112 | &prefix_command)) |
1323 | prefix_command = GNUNET_strdup (""); | 1113 | prefix_command = GNUNET_strdup (""); |
1324 | if (GNUNET_OK != | 1114 | if (GNUNET_OK != |
1325 | GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX", | 1115 | GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "GLOBAL_POSTFIX", |
1326 | &final_option)) | 1116 | &final_option)) |
1327 | final_option = GNUNET_strdup (""); | 1117 | final_option = GNUNET_strdup (""); |
1118 | |||
1119 | GNUNET_CONFIGURATION_iterate_sections (cfg, &setup_service, NULL); | ||
1120 | |||
1328 | /* start default services... */ | 1121 | /* start default services... */ |
1329 | if (GNUNET_OK == | 1122 | if (GNUNET_OK == |
1330 | GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES", | 1123 | GNUNET_CONFIGURATION_get_value_string (cfg, "ARM", "DEFAULTSERVICES", |
1331 | &defaultservices)) | 1124 | &defaultservices)) |
1332 | { | ||
1333 | #if DEBUG_ARM | ||
1334 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "Starting default services `%s'\n", | ||
1335 | defaultservices); | ||
1336 | #endif | ||
1337 | if (0 < strlen (defaultservices)) | ||
1338 | { | 1125 | { |
1339 | pos = strtok (defaultservices, " "); | 1126 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
1340 | while (pos != NULL) | 1127 | _("Starting default services `%s'\n"), defaultservices); |
1341 | { | 1128 | if (0 < strlen (defaultservices)) |
1342 | start_service (NULL, pos, NULL); | 1129 | { |
1343 | pos = strtok (NULL, " "); | 1130 | for (pos = strtok (defaultservices, " "); NULL != pos; |
1344 | } | 1131 | pos = strtok (NULL, " ")) |
1132 | { | ||
1133 | sl = find_service (pos); | ||
1134 | if (NULL == sl) | ||
1135 | { | ||
1136 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1137 | _ | ||
1138 | ("Default service `%s' not configured correctly!\n"), | ||
1139 | pos); | ||
1140 | continue; | ||
1141 | } | ||
1142 | sl->is_default = GNUNET_YES; | ||
1143 | start_process (sl); | ||
1144 | } | ||
1145 | } | ||
1146 | GNUNET_free (defaultservices); | ||
1345 | } | 1147 | } |
1346 | GNUNET_free (defaultservices); | ||
1347 | } | ||
1348 | else | 1148 | else |
1349 | { | 1149 | { |
1350 | #if DEBUG_ARM | 1150 | GNUNET_log (GNUNET_ERROR_TYPE_INFO, |
1351 | GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, "No default services configured.\n"); | 1151 | _ |
1352 | #endif | 1152 | ("No default services configured, GNUnet will not really start right now.\n")); |
1353 | } | 1153 | } |
1354 | |||
1355 | /* create listening sockets for future services */ | ||
1356 | prepare_services (cfg); | ||
1357 | 1154 | ||
1358 | /* process client requests */ | 1155 | /* process client requests */ |
1359 | GNUNET_SERVER_add_handlers (server, handlers); | 1156 | GNUNET_SERVER_add_handlers (server, handlers); |
1360 | |||
1361 | /* manage services */ | ||
1362 | GNUNET_SCHEDULER_add_with_priority (GNUNET_SCHEDULER_PRIORITY_IDLE, | ||
1363 | &config_change_task, NULL); | ||
1364 | } | 1157 | } |
1365 | 1158 | ||
1366 | 1159 | ||
@@ -1380,10 +1173,10 @@ main (int argc, char *const *argv) | |||
1380 | sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO); | 1173 | sigpipe = GNUNET_DISK_pipe (GNUNET_NO, GNUNET_NO, GNUNET_NO); |
1381 | GNUNET_assert (sigpipe != NULL); | 1174 | GNUNET_assert (sigpipe != NULL); |
1382 | shc_chld = | 1175 | shc_chld = |
1383 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); | 1176 | GNUNET_SIGNAL_handler_install (GNUNET_SIGCHLD, &sighandler_child_death); |
1384 | ret = | 1177 | ret = |
1385 | (GNUNET_OK == | 1178 | (GNUNET_OK == |
1386 | GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1; | 1179 | GNUNET_SERVICE_run (argc, argv, "arm", GNUNET_YES, &run, NULL)) ? 0 : 1; |
1387 | GNUNET_SIGNAL_handler_uninstall (shc_chld); | 1180 | GNUNET_SIGNAL_handler_uninstall (shc_chld); |
1388 | shc_chld = NULL; | 1181 | shc_chld = NULL; |
1389 | GNUNET_DISK_pipe_close (sigpipe); | 1182 | GNUNET_DISK_pipe_close (sigpipe); |
@@ -1391,6 +1184,7 @@ main (int argc, char *const *argv) | |||
1391 | return ret; | 1184 | return ret; |
1392 | } | 1185 | } |
1393 | 1186 | ||
1187 | |||
1394 | #ifdef LINUX | 1188 | #ifdef LINUX |
1395 | #include <malloc.h> | 1189 | #include <malloc.h> |
1396 | 1190 | ||