diff options
author | Christian Grothoff <christian@grothoff.org> | 2017-03-17 11:53:24 +0100 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2017-03-17 11:53:24 +0100 |
commit | c253a40bae417335fab8446f3c7182c8c5d4833f (patch) | |
tree | 0517ab35f90bf290744e5a9ddfeb75ca9547cdbe /src/transport/tcp_service_legacy.c | |
parent | 273188e646ed4d91ced1dac443e976336be877b4 (diff) | |
parent | 79fb947eb8fba243ea65e19b40b65e04f8806865 (diff) | |
download | gnunet-c253a40bae417335fab8446f3c7182c8c5d4833f.tar.gz gnunet-c253a40bae417335fab8446f3c7182c8c5d4833f.zip |
Merge branch 'master' into getopt
Diffstat (limited to 'src/transport/tcp_service_legacy.c')
-rw-r--r-- | src/transport/tcp_service_legacy.c | 1688 |
1 files changed, 1688 insertions, 0 deletions
diff --git a/src/transport/tcp_service_legacy.c b/src/transport/tcp_service_legacy.c new file mode 100644 index 000000000..050a38acc --- /dev/null +++ b/src/transport/tcp_service_legacy.c | |||
@@ -0,0 +1,1688 @@ | |||
1 | /* | ||
2 | This file is part of GNUnet. | ||
3 | Copyright (C) 2009, 2012 GNUnet e.V. | ||
4 | |||
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 | ||
7 | by the Free Software Foundation; either version 3, or (at your | ||
8 | option) any later version. | ||
9 | |||
10 | GNUnet is distributed in the hope that it will be useful, but | ||
11 | WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with GNUnet; see the file COPYING. If not, write to the | ||
17 | Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | ||
18 | Boston, MA 02110-1301, USA. | ||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file util/service.c | ||
23 | * @brief functions related to starting services | ||
24 | * @author Christian Grothoff | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "gnunet_util_lib.h" | ||
28 | #include "gnunet_protocols.h" | ||
29 | #include "gnunet_constants.h" | ||
30 | #include "gnunet_resolver_service.h" | ||
31 | |||
32 | #if HAVE_MALLINFO | ||
33 | #include <malloc.h> | ||
34 | #include "gauger.h" | ||
35 | #endif | ||
36 | |||
37 | |||
38 | /* ******************* access control ******************** */ | ||
39 | |||
40 | /** | ||
41 | * Check if the given IP address is in the list of IP addresses. | ||
42 | * | ||
43 | * @param list a list of networks | ||
44 | * @param add the IP to check (in network byte order) | ||
45 | * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is | ||
46 | */ | ||
47 | static int | ||
48 | check_ipv4_listed (const struct GNUNET_STRINGS_IPv4NetworkPolicy *list, | ||
49 | const struct in_addr *add) | ||
50 | { | ||
51 | unsigned int i; | ||
52 | |||
53 | if (NULL == list) | ||
54 | return GNUNET_NO; | ||
55 | i = 0; | ||
56 | while ((list[i].network.s_addr != 0) || (list[i].netmask.s_addr != 0)) | ||
57 | { | ||
58 | if ((add->s_addr & list[i].netmask.s_addr) == | ||
59 | (list[i].network.s_addr & list[i].netmask.s_addr)) | ||
60 | return GNUNET_YES; | ||
61 | i++; | ||
62 | } | ||
63 | return GNUNET_NO; | ||
64 | } | ||
65 | |||
66 | |||
67 | /** | ||
68 | * Check if the given IP address is in the list of IP addresses. | ||
69 | * | ||
70 | * @param list a list of networks | ||
71 | * @param ip the IP to check (in network byte order) | ||
72 | * @return #GNUNET_NO if the IP is not in the list, #GNUNET_YES if it it is | ||
73 | */ | ||
74 | static int | ||
75 | check_ipv6_listed (const struct GNUNET_STRINGS_IPv6NetworkPolicy *list, | ||
76 | const struct in6_addr *ip) | ||
77 | { | ||
78 | unsigned int i; | ||
79 | unsigned int j; | ||
80 | struct in6_addr zero; | ||
81 | |||
82 | if (NULL == list) | ||
83 | return GNUNET_NO; | ||
84 | memset (&zero, 0, sizeof (struct in6_addr)); | ||
85 | i = 0; | ||
86 | NEXT: | ||
87 | while (0 != memcmp (&zero, &list[i].network, sizeof (struct in6_addr))) | ||
88 | { | ||
89 | for (j = 0; j < sizeof (struct in6_addr) / sizeof (int); j++) | ||
90 | if (((((int *) ip)[j] & ((int *) &list[i].netmask)[j])) != | ||
91 | (((int *) &list[i].network)[j] & ((int *) &list[i].netmask)[j])) | ||
92 | { | ||
93 | i++; | ||
94 | goto NEXT; | ||
95 | } | ||
96 | return GNUNET_YES; | ||
97 | } | ||
98 | return GNUNET_NO; | ||
99 | } | ||
100 | |||
101 | |||
102 | /* ****************** service struct ****************** */ | ||
103 | |||
104 | |||
105 | /** | ||
106 | * Context for "service_task". | ||
107 | */ | ||
108 | struct GNUNET_SERVICE_Context | ||
109 | { | ||
110 | /** | ||
111 | * Our configuration. | ||
112 | */ | ||
113 | const struct GNUNET_CONFIGURATION_Handle *cfg; | ||
114 | |||
115 | /** | ||
116 | * Handle for the server. | ||
117 | */ | ||
118 | struct GNUNET_SERVER_Handle *server; | ||
119 | |||
120 | /** | ||
121 | * NULL-terminated array of addresses to bind to, NULL if we got pre-bound | ||
122 | * listen sockets. | ||
123 | */ | ||
124 | struct sockaddr **addrs; | ||
125 | |||
126 | /** | ||
127 | * Name of our service. | ||
128 | */ | ||
129 | const char *service_name; | ||
130 | |||
131 | /** | ||
132 | * Main service-specific task to run. | ||
133 | */ | ||
134 | GNUNET_SERVICE_Main task; | ||
135 | |||
136 | /** | ||
137 | * Closure for @e task. | ||
138 | */ | ||
139 | void *task_cls; | ||
140 | |||
141 | /** | ||
142 | * IPv4 addresses that are not allowed to connect. | ||
143 | */ | ||
144 | struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_denied; | ||
145 | |||
146 | /** | ||
147 | * IPv6 addresses that are not allowed to connect. | ||
148 | */ | ||
149 | struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_denied; | ||
150 | |||
151 | /** | ||
152 | * IPv4 addresses that are allowed to connect (if not | ||
153 | * set, all are allowed). | ||
154 | */ | ||
155 | struct GNUNET_STRINGS_IPv4NetworkPolicy *v4_allowed; | ||
156 | |||
157 | /** | ||
158 | * IPv6 addresses that are allowed to connect (if not | ||
159 | * set, all are allowed). | ||
160 | */ | ||
161 | struct GNUNET_STRINGS_IPv6NetworkPolicy *v6_allowed; | ||
162 | |||
163 | /** | ||
164 | * My (default) message handlers. Adjusted copy | ||
165 | * of "defhandlers". | ||
166 | */ | ||
167 | struct GNUNET_SERVER_MessageHandler *my_handlers; | ||
168 | |||
169 | /** | ||
170 | * Array of the lengths of the entries in addrs. | ||
171 | */ | ||
172 | socklen_t *addrlens; | ||
173 | |||
174 | /** | ||
175 | * NULL-terminated array of listen sockets we should take over. | ||
176 | */ | ||
177 | struct GNUNET_NETWORK_Handle **lsocks; | ||
178 | |||
179 | /** | ||
180 | * Task ID of the shutdown task. | ||
181 | */ | ||
182 | struct GNUNET_SCHEDULER_Task *shutdown_task; | ||
183 | |||
184 | /** | ||
185 | * Idle timeout for server. | ||
186 | */ | ||
187 | struct GNUNET_TIME_Relative timeout; | ||
188 | |||
189 | /** | ||
190 | * Overall success/failure of the service start. | ||
191 | */ | ||
192 | int ret; | ||
193 | |||
194 | /** | ||
195 | * If we are daemonizing, this FD is set to the | ||
196 | * pipe to the parent. Send '.' if we started | ||
197 | * ok, '!' if not. -1 if we are not daemonizing. | ||
198 | */ | ||
199 | int ready_confirm_fd; | ||
200 | |||
201 | /** | ||
202 | * Do we close connections if we receive messages | ||
203 | * for which we have no handler? | ||
204 | */ | ||
205 | int require_found; | ||
206 | |||
207 | /** | ||
208 | * Do we require a matching UID for UNIX domain socket connections? | ||
209 | * #GNUNET_NO means that the UID does not have to match (however, | ||
210 | * @e match_gid may still impose other access control checks). | ||
211 | */ | ||
212 | int match_uid; | ||
213 | |||
214 | /** | ||
215 | * Do we require a matching GID for UNIX domain socket connections? | ||
216 | * Ignored if @e match_uid is #GNUNET_YES. Note that this is about | ||
217 | * checking that the client's UID is in our group OR that the | ||
218 | * client's GID is our GID. If both "match_gid" and @e match_uid are | ||
219 | * #GNUNET_NO, all users on the local system have access. | ||
220 | */ | ||
221 | int match_gid; | ||
222 | |||
223 | /** | ||
224 | * Our options. | ||
225 | */ | ||
226 | enum GNUNET_SERVICE_Options options; | ||
227 | |||
228 | }; | ||
229 | |||
230 | |||
231 | /* ****************** message handlers ****************** */ | ||
232 | |||
233 | /** | ||
234 | * Send a 'TEST' message back to the client. | ||
235 | * | ||
236 | * @param cls the 'struct GNUNET_SERVER_Client' to send TEST to | ||
237 | * @param size number of bytes available in 'buf' | ||
238 | * @param buf where to copy the message | ||
239 | * @return number of bytes written to 'buf' | ||
240 | */ | ||
241 | static size_t | ||
242 | write_test (void *cls, size_t size, void *buf) | ||
243 | { | ||
244 | struct GNUNET_SERVER_Client *client = cls; | ||
245 | struct GNUNET_MessageHeader *msg; | ||
246 | |||
247 | if (size < sizeof (struct GNUNET_MessageHeader)) | ||
248 | { | ||
249 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
250 | return 0; /* client disconnected */ | ||
251 | } | ||
252 | msg = (struct GNUNET_MessageHeader *) buf; | ||
253 | msg->type = htons (GNUNET_MESSAGE_TYPE_TEST); | ||
254 | msg->size = htons (sizeof (struct GNUNET_MessageHeader)); | ||
255 | GNUNET_SERVER_receive_done (client, GNUNET_OK); | ||
256 | return sizeof (struct GNUNET_MessageHeader); | ||
257 | } | ||
258 | |||
259 | |||
260 | /** | ||
261 | * Handler for TEST message. | ||
262 | * | ||
263 | * @param cls closure (refers to service) | ||
264 | * @param client identification of the client | ||
265 | * @param message the actual message | ||
266 | */ | ||
267 | static void | ||
268 | handle_test (void *cls, struct GNUNET_SERVER_Client *client, | ||
269 | const struct GNUNET_MessageHeader *message) | ||
270 | { | ||
271 | /* simply bounce message back to acknowledge */ | ||
272 | if (NULL == | ||
273 | GNUNET_SERVER_notify_transmit_ready (client, | ||
274 | sizeof (struct GNUNET_MessageHeader), | ||
275 | GNUNET_TIME_UNIT_FOREVER_REL, | ||
276 | &write_test, client)) | ||
277 | GNUNET_SERVER_receive_done (client, GNUNET_SYSERR); | ||
278 | } | ||
279 | |||
280 | |||
281 | /** | ||
282 | * Default handlers for all services. Will be copied and the | ||
283 | * "callback_cls" fields will be replaced with the specific service | ||
284 | * struct. | ||
285 | */ | ||
286 | static const struct GNUNET_SERVER_MessageHandler defhandlers[] = { | ||
287 | {&handle_test, NULL, GNUNET_MESSAGE_TYPE_TEST, | ||
288 | sizeof (struct GNUNET_MessageHeader)}, | ||
289 | {NULL, NULL, 0, 0} | ||
290 | }; | ||
291 | |||
292 | |||
293 | /* ****************** service core routines ************** */ | ||
294 | |||
295 | |||
296 | /** | ||
297 | * Check if access to the service is allowed from the given address. | ||
298 | * | ||
299 | * @param cls closure | ||
300 | * @param uc credentials, if available, otherwise NULL | ||
301 | * @param addr address | ||
302 | * @param addrlen length of address | ||
303 | * @return #GNUNET_YES to allow, #GNUNET_NO to deny, #GNUNET_SYSERR | ||
304 | * for unknown address family (will be denied). | ||
305 | */ | ||
306 | static int | ||
307 | check_access (void *cls, const struct GNUNET_CONNECTION_Credentials *uc, | ||
308 | const struct sockaddr *addr, socklen_t addrlen) | ||
309 | { | ||
310 | struct GNUNET_SERVICE_Context *sctx = cls; | ||
311 | const struct sockaddr_in *i4; | ||
312 | const struct sockaddr_in6 *i6; | ||
313 | int ret; | ||
314 | |||
315 | switch (addr->sa_family) | ||
316 | { | ||
317 | case AF_INET: | ||
318 | GNUNET_assert (addrlen == sizeof (struct sockaddr_in)); | ||
319 | i4 = (const struct sockaddr_in *) addr; | ||
320 | ret = ((NULL == sctx->v4_allowed) || | ||
321 | (check_ipv4_listed (sctx->v4_allowed, &i4->sin_addr))) && | ||
322 | ((NULL == sctx->v4_denied) || | ||
323 | (!check_ipv4_listed (sctx->v4_denied, &i4->sin_addr))); | ||
324 | break; | ||
325 | case AF_INET6: | ||
326 | GNUNET_assert (addrlen == sizeof (struct sockaddr_in6)); | ||
327 | i6 = (const struct sockaddr_in6 *) addr; | ||
328 | ret = ((NULL == sctx->v6_allowed) || | ||
329 | (check_ipv6_listed (sctx->v6_allowed, &i6->sin6_addr))) && | ||
330 | ((NULL == sctx->v6_denied) || | ||
331 | (!check_ipv6_listed (sctx->v6_denied, &i6->sin6_addr))); | ||
332 | break; | ||
333 | #ifndef WINDOWS | ||
334 | case AF_UNIX: | ||
335 | ret = GNUNET_OK; /* controlled using file-system ACL now */ | ||
336 | break; | ||
337 | #endif | ||
338 | default: | ||
339 | LOG (GNUNET_ERROR_TYPE_WARNING, _("Unknown address family %d\n"), | ||
340 | addr->sa_family); | ||
341 | return GNUNET_SYSERR; | ||
342 | } | ||
343 | if (GNUNET_OK != ret) | ||
344 | { | ||
345 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
346 | _("Access from `%s' denied to service `%s'\n"), | ||
347 | GNUNET_a2s (addr, addrlen), | ||
348 | sctx->service_name); | ||
349 | } | ||
350 | return ret; | ||
351 | } | ||
352 | |||
353 | |||
354 | /** | ||
355 | * Get the name of the file where we will | ||
356 | * write the PID of the service. | ||
357 | * | ||
358 | * @param sctx service context | ||
359 | * @return name of the file for the process ID | ||
360 | */ | ||
361 | static char * | ||
362 | get_pid_file_name (struct GNUNET_SERVICE_Context *sctx) | ||
363 | { | ||
364 | char *pif; | ||
365 | |||
366 | if (GNUNET_OK != | ||
367 | GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name, | ||
368 | "PIDFILE", &pif)) | ||
369 | return NULL; | ||
370 | return pif; | ||
371 | } | ||
372 | |||
373 | |||
374 | /** | ||
375 | * Parse an IPv4 access control list. | ||
376 | * | ||
377 | * @param ret location where to write the ACL (set) | ||
378 | * @param sctx service context to use to get the configuration | ||
379 | * @param option name of the ACL option to parse | ||
380 | * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including | ||
381 | * no ACL configured) | ||
382 | */ | ||
383 | static int | ||
384 | process_acl4 (struct GNUNET_STRINGS_IPv4NetworkPolicy **ret, | ||
385 | struct GNUNET_SERVICE_Context *sctx, | ||
386 | const char *option) | ||
387 | { | ||
388 | char *opt; | ||
389 | |||
390 | if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option)) | ||
391 | { | ||
392 | *ret = NULL; | ||
393 | return GNUNET_OK; | ||
394 | } | ||
395 | GNUNET_break (GNUNET_OK == | ||
396 | GNUNET_CONFIGURATION_get_value_string (sctx->cfg, | ||
397 | sctx->service_name, | ||
398 | option, &opt)); | ||
399 | if (NULL == (*ret = GNUNET_STRINGS_parse_ipv4_policy (opt))) | ||
400 | { | ||
401 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
402 | _("Could not parse IPv4 network specification `%s' for `%s:%s'\n"), | ||
403 | opt, sctx->service_name, option); | ||
404 | GNUNET_free (opt); | ||
405 | return GNUNET_SYSERR; | ||
406 | } | ||
407 | GNUNET_free (opt); | ||
408 | return GNUNET_OK; | ||
409 | } | ||
410 | |||
411 | |||
412 | /** | ||
413 | * Parse an IPv6 access control list. | ||
414 | * | ||
415 | * @param ret location where to write the ACL (set) | ||
416 | * @param sctx service context to use to get the configuration | ||
417 | * @param option name of the ACL option to parse | ||
418 | * @return #GNUNET_SYSERR on parse error, #GNUNET_OK on success (including | ||
419 | * no ACL configured) | ||
420 | */ | ||
421 | static int | ||
422 | process_acl6 (struct GNUNET_STRINGS_IPv6NetworkPolicy **ret, | ||
423 | struct GNUNET_SERVICE_Context *sctx, | ||
424 | const char *option) | ||
425 | { | ||
426 | char *opt; | ||
427 | |||
428 | if (!GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, option)) | ||
429 | { | ||
430 | *ret = NULL; | ||
431 | return GNUNET_OK; | ||
432 | } | ||
433 | GNUNET_break (GNUNET_OK == | ||
434 | GNUNET_CONFIGURATION_get_value_string (sctx->cfg, | ||
435 | sctx->service_name, | ||
436 | option, &opt)); | ||
437 | if (NULL == (*ret = GNUNET_STRINGS_parse_ipv6_policy (opt))) | ||
438 | { | ||
439 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
440 | _("Could not parse IPv6 network specification `%s' for `%s:%s'\n"), | ||
441 | opt, sctx->service_name, option); | ||
442 | GNUNET_free (opt); | ||
443 | return GNUNET_SYSERR; | ||
444 | } | ||
445 | GNUNET_free (opt); | ||
446 | return GNUNET_OK; | ||
447 | } | ||
448 | |||
449 | |||
450 | /** | ||
451 | * Add the given UNIX domain path as an address to the | ||
452 | * list (as the first entry). | ||
453 | * | ||
454 | * @param saddrs array to update | ||
455 | * @param saddrlens where to store the address length | ||
456 | * @param unixpath path to add | ||
457 | * @param abstract #GNUNET_YES to add an abstract UNIX domain socket. This | ||
458 | * parameter is ignore on systems other than LINUX | ||
459 | */ | ||
460 | static void | ||
461 | add_unixpath (struct sockaddr **saddrs, | ||
462 | socklen_t *saddrlens, | ||
463 | const char *unixpath, | ||
464 | int abstract) | ||
465 | { | ||
466 | #ifdef AF_UNIX | ||
467 | struct sockaddr_un *un; | ||
468 | |||
469 | un = GNUNET_new (struct sockaddr_un); | ||
470 | un->sun_family = AF_UNIX; | ||
471 | strncpy (un->sun_path, unixpath, sizeof (un->sun_path) - 1); | ||
472 | #ifdef LINUX | ||
473 | if (GNUNET_YES == abstract) | ||
474 | un->sun_path[0] = '\0'; | ||
475 | #endif | ||
476 | #if HAVE_SOCKADDR_UN_SUN_LEN | ||
477 | un->sun_len = (u_char) sizeof (struct sockaddr_un); | ||
478 | #endif | ||
479 | *saddrs = (struct sockaddr *) un; | ||
480 | *saddrlens = sizeof (struct sockaddr_un); | ||
481 | #else | ||
482 | /* this function should never be called | ||
483 | * unless AF_UNIX is defined! */ | ||
484 | GNUNET_assert (0); | ||
485 | #endif | ||
486 | } | ||
487 | |||
488 | |||
489 | /** | ||
490 | * Get the list of addresses that a server for the given service | ||
491 | * should bind to. | ||
492 | * | ||
493 | * @param service_name name of the service | ||
494 | * @param cfg configuration (which specifies the addresses) | ||
495 | * @param addrs set (call by reference) to an array of pointers to the | ||
496 | * addresses the server should bind to and listen on; the | ||
497 | * array will be NULL-terminated (on success) | ||
498 | * @param addr_lens set (call by reference) to an array of the lengths | ||
499 | * of the respective `struct sockaddr` struct in the @a addrs | ||
500 | * array (on success) | ||
501 | * @return number of addresses found on success, | ||
502 | * #GNUNET_SYSERR if the configuration | ||
503 | * did not specify reasonable finding information or | ||
504 | * if it specified a hostname that could not be resolved; | ||
505 | * #GNUNET_NO if the number of addresses configured is | ||
506 | * zero (in this case, `*addrs` and `*addr_lens` will be | ||
507 | * set to NULL). | ||
508 | */ | ||
509 | int | ||
510 | GNUNET_SERVICE_get_server_addresses (const char *service_name, | ||
511 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
512 | struct sockaddr ***addrs, | ||
513 | socklen_t ** addr_lens) | ||
514 | { | ||
515 | int disablev6; | ||
516 | struct GNUNET_NETWORK_Handle *desc; | ||
517 | unsigned long long port; | ||
518 | char *unixpath; | ||
519 | struct addrinfo hints; | ||
520 | struct addrinfo *res; | ||
521 | struct addrinfo *pos; | ||
522 | struct addrinfo *next; | ||
523 | unsigned int i; | ||
524 | int resi; | ||
525 | int ret; | ||
526 | int abstract; | ||
527 | struct sockaddr **saddrs; | ||
528 | socklen_t *saddrlens; | ||
529 | char *hostname; | ||
530 | |||
531 | *addrs = NULL; | ||
532 | *addr_lens = NULL; | ||
533 | desc = NULL; | ||
534 | if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "DISABLEV6")) | ||
535 | { | ||
536 | if (GNUNET_SYSERR == | ||
537 | (disablev6 = | ||
538 | GNUNET_CONFIGURATION_get_value_yesno (cfg, service_name, "DISABLEV6"))) | ||
539 | return GNUNET_SYSERR; | ||
540 | } | ||
541 | else | ||
542 | disablev6 = GNUNET_NO; | ||
543 | |||
544 | if (! disablev6) | ||
545 | { | ||
546 | /* probe IPv6 support */ | ||
547 | desc = GNUNET_NETWORK_socket_create (PF_INET6, SOCK_STREAM, 0); | ||
548 | if (NULL == desc) | ||
549 | { | ||
550 | if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || | ||
551 | (EACCES == errno)) | ||
552 | { | ||
553 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); | ||
554 | return GNUNET_SYSERR; | ||
555 | } | ||
556 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
557 | _("Disabling IPv6 support for service `%s', failed to create IPv6 socket: %s\n"), | ||
558 | service_name, STRERROR (errno)); | ||
559 | disablev6 = GNUNET_YES; | ||
560 | } | ||
561 | else | ||
562 | { | ||
563 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); | ||
564 | desc = NULL; | ||
565 | } | ||
566 | } | ||
567 | |||
568 | port = 0; | ||
569 | if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "PORT")) | ||
570 | { | ||
571 | if (GNUNET_OK != | ||
572 | GNUNET_CONFIGURATION_get_value_number (cfg, service_name, | ||
573 | "PORT", &port)) | ||
574 | { | ||
575 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
576 | _("Require valid port number for service `%s' in configuration!\n"), | ||
577 | service_name); | ||
578 | } | ||
579 | if (port > 65535) | ||
580 | { | ||
581 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
582 | _("Require valid port number for service `%s' in configuration!\n"), | ||
583 | service_name); | ||
584 | return GNUNET_SYSERR; | ||
585 | } | ||
586 | } | ||
587 | |||
588 | if (GNUNET_CONFIGURATION_have_value (cfg, service_name, "BINDTO")) | ||
589 | { | ||
590 | GNUNET_break (GNUNET_OK == | ||
591 | GNUNET_CONFIGURATION_get_value_string (cfg, service_name, | ||
592 | "BINDTO", &hostname)); | ||
593 | } | ||
594 | else | ||
595 | hostname = NULL; | ||
596 | |||
597 | unixpath = NULL; | ||
598 | abstract = GNUNET_NO; | ||
599 | #ifdef AF_UNIX | ||
600 | if ((GNUNET_YES == | ||
601 | GNUNET_CONFIGURATION_have_value (cfg, service_name, "UNIXPATH")) && | ||
602 | (GNUNET_OK == | ||
603 | GNUNET_CONFIGURATION_get_value_filename (cfg, service_name, "UNIXPATH", | ||
604 | &unixpath)) && | ||
605 | (0 < strlen (unixpath))) | ||
606 | { | ||
607 | /* probe UNIX support */ | ||
608 | struct sockaddr_un s_un; | ||
609 | |||
610 | if (strlen (unixpath) >= sizeof (s_un.sun_path)) | ||
611 | { | ||
612 | LOG (GNUNET_ERROR_TYPE_WARNING, | ||
613 | _("UNIXPATH `%s' too long, maximum length is %llu\n"), unixpath, | ||
614 | (unsigned long long) sizeof (s_un.sun_path)); | ||
615 | unixpath = GNUNET_NETWORK_shorten_unixpath (unixpath); | ||
616 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
617 | _("Using `%s' instead\n"), | ||
618 | unixpath); | ||
619 | } | ||
620 | #ifdef LINUX | ||
621 | abstract = GNUNET_CONFIGURATION_get_value_yesno (cfg, | ||
622 | "TESTING", | ||
623 | "USE_ABSTRACT_SOCKETS"); | ||
624 | if (GNUNET_SYSERR == abstract) | ||
625 | abstract = GNUNET_NO; | ||
626 | #endif | ||
627 | if ((GNUNET_YES != abstract) | ||
628 | && (GNUNET_OK != | ||
629 | GNUNET_DISK_directory_create_for_file (unixpath))) | ||
630 | GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, | ||
631 | "mkdir", | ||
632 | unixpath); | ||
633 | } | ||
634 | if (NULL != unixpath) | ||
635 | { | ||
636 | desc = GNUNET_NETWORK_socket_create (AF_UNIX, SOCK_STREAM, 0); | ||
637 | if (NULL == desc) | ||
638 | { | ||
639 | if ((ENOBUFS == errno) || (ENOMEM == errno) || (ENFILE == errno) || | ||
640 | (EACCES == errno)) | ||
641 | { | ||
642 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "socket"); | ||
643 | GNUNET_free_non_null (hostname); | ||
644 | GNUNET_free (unixpath); | ||
645 | return GNUNET_SYSERR; | ||
646 | } | ||
647 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
648 | _("Disabling UNIX domain socket support for service `%s', failed to create UNIX domain socket: %s\n"), | ||
649 | service_name, | ||
650 | STRERROR (errno)); | ||
651 | GNUNET_free (unixpath); | ||
652 | unixpath = NULL; | ||
653 | } | ||
654 | else | ||
655 | { | ||
656 | GNUNET_break (GNUNET_OK == GNUNET_NETWORK_socket_close (desc)); | ||
657 | desc = NULL; | ||
658 | } | ||
659 | } | ||
660 | #endif | ||
661 | |||
662 | if ((0 == port) && (NULL == unixpath)) | ||
663 | { | ||
664 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
665 | _("Have neither PORT nor UNIXPATH for service `%s', but one is required\n"), | ||
666 | service_name); | ||
667 | GNUNET_free_non_null (hostname); | ||
668 | return GNUNET_SYSERR; | ||
669 | } | ||
670 | if (0 == port) | ||
671 | { | ||
672 | saddrs = GNUNET_malloc (2 * sizeof (struct sockaddr *)); | ||
673 | saddrlens = GNUNET_malloc (2 * sizeof (socklen_t)); | ||
674 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
675 | GNUNET_free_non_null (unixpath); | ||
676 | GNUNET_free_non_null (hostname); | ||
677 | *addrs = saddrs; | ||
678 | *addr_lens = saddrlens; | ||
679 | return 1; | ||
680 | } | ||
681 | |||
682 | if (NULL != hostname) | ||
683 | { | ||
684 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
685 | "Resolving `%s' since that is where `%s' will bind to.\n", | ||
686 | hostname, | ||
687 | service_name); | ||
688 | memset (&hints, 0, sizeof (struct addrinfo)); | ||
689 | if (disablev6) | ||
690 | hints.ai_family = AF_INET; | ||
691 | hints.ai_protocol = IPPROTO_TCP; | ||
692 | if ((0 != (ret = getaddrinfo (hostname, NULL, &hints, &res))) || | ||
693 | (NULL == res)) | ||
694 | { | ||
695 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
696 | _("Failed to resolve `%s': %s\n"), | ||
697 | hostname, | ||
698 | gai_strerror (ret)); | ||
699 | GNUNET_free (hostname); | ||
700 | GNUNET_free_non_null (unixpath); | ||
701 | return GNUNET_SYSERR; | ||
702 | } | ||
703 | next = res; | ||
704 | i = 0; | ||
705 | while (NULL != (pos = next)) | ||
706 | { | ||
707 | next = pos->ai_next; | ||
708 | if ((disablev6) && (pos->ai_family == AF_INET6)) | ||
709 | continue; | ||
710 | i++; | ||
711 | } | ||
712 | if (0 == i) | ||
713 | { | ||
714 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
715 | _("Failed to find %saddress for `%s'.\n"), | ||
716 | disablev6 ? "IPv4 " : "", | ||
717 | hostname); | ||
718 | freeaddrinfo (res); | ||
719 | GNUNET_free (hostname); | ||
720 | GNUNET_free_non_null (unixpath); | ||
721 | return GNUNET_SYSERR; | ||
722 | } | ||
723 | resi = i; | ||
724 | if (NULL != unixpath) | ||
725 | resi++; | ||
726 | saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); | ||
727 | saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); | ||
728 | i = 0; | ||
729 | if (NULL != unixpath) | ||
730 | { | ||
731 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
732 | i++; | ||
733 | } | ||
734 | next = res; | ||
735 | while (NULL != (pos = next)) | ||
736 | { | ||
737 | next = pos->ai_next; | ||
738 | if ((disablev6) && (AF_INET6 == pos->ai_family)) | ||
739 | continue; | ||
740 | if ((IPPROTO_TCP != pos->ai_protocol) && (0 != pos->ai_protocol)) | ||
741 | continue; /* not TCP */ | ||
742 | if ((SOCK_STREAM != pos->ai_socktype) && (0 != pos->ai_socktype)) | ||
743 | continue; /* huh? */ | ||
744 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Service `%s' will bind to `%s'\n", | ||
745 | service_name, GNUNET_a2s (pos->ai_addr, pos->ai_addrlen)); | ||
746 | if (AF_INET == pos->ai_family) | ||
747 | { | ||
748 | GNUNET_assert (sizeof (struct sockaddr_in) == pos->ai_addrlen); | ||
749 | saddrlens[i] = pos->ai_addrlen; | ||
750 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
751 | GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); | ||
752 | ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); | ||
753 | } | ||
754 | else | ||
755 | { | ||
756 | GNUNET_assert (AF_INET6 == pos->ai_family); | ||
757 | GNUNET_assert (sizeof (struct sockaddr_in6) == pos->ai_addrlen); | ||
758 | saddrlens[i] = pos->ai_addrlen; | ||
759 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
760 | GNUNET_memcpy (saddrs[i], pos->ai_addr, saddrlens[i]); | ||
761 | ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); | ||
762 | } | ||
763 | i++; | ||
764 | } | ||
765 | GNUNET_free (hostname); | ||
766 | freeaddrinfo (res); | ||
767 | resi = i; | ||
768 | } | ||
769 | else | ||
770 | { | ||
771 | /* will bind against everything, just set port */ | ||
772 | if (disablev6) | ||
773 | { | ||
774 | /* V4-only */ | ||
775 | resi = 1; | ||
776 | if (NULL != unixpath) | ||
777 | resi++; | ||
778 | i = 0; | ||
779 | saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); | ||
780 | saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); | ||
781 | if (NULL != unixpath) | ||
782 | { | ||
783 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
784 | i++; | ||
785 | } | ||
786 | saddrlens[i] = sizeof (struct sockaddr_in); | ||
787 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
788 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
789 | ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[i]; | ||
790 | #endif | ||
791 | ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; | ||
792 | ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); | ||
793 | } | ||
794 | else | ||
795 | { | ||
796 | /* dual stack */ | ||
797 | resi = 2; | ||
798 | if (NULL != unixpath) | ||
799 | resi++; | ||
800 | saddrs = GNUNET_malloc ((resi + 1) * sizeof (struct sockaddr *)); | ||
801 | saddrlens = GNUNET_malloc ((resi + 1) * sizeof (socklen_t)); | ||
802 | i = 0; | ||
803 | if (NULL != unixpath) | ||
804 | { | ||
805 | add_unixpath (saddrs, saddrlens, unixpath, abstract); | ||
806 | i++; | ||
807 | } | ||
808 | saddrlens[i] = sizeof (struct sockaddr_in6); | ||
809 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
810 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
811 | ((struct sockaddr_in6 *) saddrs[i])->sin6_len = saddrlens[0]; | ||
812 | #endif | ||
813 | ((struct sockaddr_in6 *) saddrs[i])->sin6_family = AF_INET6; | ||
814 | ((struct sockaddr_in6 *) saddrs[i])->sin6_port = htons (port); | ||
815 | i++; | ||
816 | saddrlens[i] = sizeof (struct sockaddr_in); | ||
817 | saddrs[i] = GNUNET_malloc (saddrlens[i]); | ||
818 | #if HAVE_SOCKADDR_IN_SIN_LEN | ||
819 | ((struct sockaddr_in *) saddrs[i])->sin_len = saddrlens[1]; | ||
820 | #endif | ||
821 | ((struct sockaddr_in *) saddrs[i])->sin_family = AF_INET; | ||
822 | ((struct sockaddr_in *) saddrs[i])->sin_port = htons (port); | ||
823 | } | ||
824 | } | ||
825 | GNUNET_free_non_null (unixpath); | ||
826 | *addrs = saddrs; | ||
827 | *addr_lens = saddrlens; | ||
828 | return resi; | ||
829 | } | ||
830 | |||
831 | |||
832 | #ifdef MINGW | ||
833 | /** | ||
834 | * Read listen sockets from the parent process (ARM). | ||
835 | * | ||
836 | * @param sctx service context to initialize | ||
837 | * @return #GNUNET_YES if ok, #GNUNET_NO if not ok (must bind yourself), | ||
838 | * and #GNUNET_SYSERR on error. | ||
839 | */ | ||
840 | static int | ||
841 | receive_sockets_from_parent (struct GNUNET_SERVICE_Context *sctx) | ||
842 | { | ||
843 | const char *env_buf; | ||
844 | int fail; | ||
845 | uint64_t count; | ||
846 | uint64_t i; | ||
847 | HANDLE lsocks_pipe; | ||
848 | |||
849 | env_buf = getenv ("GNUNET_OS_READ_LSOCKS"); | ||
850 | if ((NULL == env_buf) || (strlen (env_buf) <= 0)) | ||
851 | return GNUNET_NO; | ||
852 | /* Using W32 API directly here, because this pipe will | ||
853 | * never be used outside of this function, and it's just too much of a bother | ||
854 | * to create a GNUnet API that boxes a HANDLE (the way it is done with socks) | ||
855 | */ | ||
856 | lsocks_pipe = (HANDLE) strtoul (env_buf, NULL, 10); | ||
857 | if ( (0 == lsocks_pipe) || (INVALID_HANDLE_VALUE == lsocks_pipe)) | ||
858 | return GNUNET_NO; | ||
859 | fail = 1; | ||
860 | do | ||
861 | { | ||
862 | int ret; | ||
863 | int fail2; | ||
864 | DWORD rd; | ||
865 | |||
866 | ret = ReadFile (lsocks_pipe, &count, sizeof (count), &rd, NULL); | ||
867 | if ((0 == ret) || (sizeof (count) != rd) || (0 == count)) | ||
868 | break; | ||
869 | sctx->lsocks = | ||
870 | GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (count + 1)); | ||
871 | |||
872 | fail2 = 1; | ||
873 | for (i = 0; i < count; i++) | ||
874 | { | ||
875 | WSAPROTOCOL_INFOA pi; | ||
876 | uint64_t size; | ||
877 | SOCKET s; | ||
878 | |||
879 | ret = ReadFile (lsocks_pipe, &size, sizeof (size), &rd, NULL); | ||
880 | if ( (0 == ret) || (sizeof (size) != rd) || (sizeof (pi) != size) ) | ||
881 | break; | ||
882 | ret = ReadFile (lsocks_pipe, &pi, sizeof (pi), &rd, NULL); | ||
883 | if ( (0 == ret) || (sizeof (pi) != rd)) | ||
884 | break; | ||
885 | s = WSASocketA (pi.iAddressFamily, pi.iSocketType, pi.iProtocol, &pi, 0, WSA_FLAG_OVERLAPPED); | ||
886 | sctx->lsocks[i] = GNUNET_NETWORK_socket_box_native (s); | ||
887 | if (NULL == sctx->lsocks[i]) | ||
888 | break; | ||
889 | else if (i == count - 1) | ||
890 | fail2 = 0; | ||
891 | } | ||
892 | if (fail2) | ||
893 | break; | ||
894 | sctx->lsocks[count] = NULL; | ||
895 | fail = 0; | ||
896 | } | ||
897 | while (fail); | ||
898 | |||
899 | CloseHandle (lsocks_pipe); | ||
900 | |||
901 | if (fail) | ||
902 | { | ||
903 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
904 | _("Could not access a pre-bound socket, will try to bind myself\n")); | ||
905 | for (i = 0; (i < count) && (NULL != sctx->lsocks[i]); i++) | ||
906 | GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[i])); | ||
907 | GNUNET_free_non_null (sctx->lsocks); | ||
908 | sctx->lsocks = NULL; | ||
909 | return GNUNET_NO; | ||
910 | } | ||
911 | return GNUNET_YES; | ||
912 | } | ||
913 | #endif | ||
914 | |||
915 | |||
916 | /** | ||
917 | * Setup addr, addrlen, idle_timeout | ||
918 | * based on configuration! | ||
919 | * | ||
920 | * Configuration may specify: | ||
921 | * - PORT (where to bind to for TCP) | ||
922 | * - UNIXPATH (where to bind to for UNIX domain sockets) | ||
923 | * - TIMEOUT (after how many ms does an inactive service timeout); | ||
924 | * - DISABLEV6 (disable support for IPv6, otherwise we use dual-stack) | ||
925 | * - BINDTO (hostname or IP address to bind to, otherwise we take everything) | ||
926 | * - ACCEPT_FROM (only allow connections from specified IPv4 subnets) | ||
927 | * - ACCEPT_FROM6 (only allow connections from specified IPv6 subnets) | ||
928 | * - REJECT_FROM (disallow allow connections from specified IPv4 subnets) | ||
929 | * - REJECT_FROM6 (disallow allow connections from specified IPv6 subnets) | ||
930 | * | ||
931 | * @param sctx service context to initialize | ||
932 | * @return #GNUNET_OK if configuration succeeded | ||
933 | */ | ||
934 | static int | ||
935 | setup_service (struct GNUNET_SERVICE_Context *sctx) | ||
936 | { | ||
937 | struct GNUNET_TIME_Relative idleout; | ||
938 | int tolerant; | ||
939 | |||
940 | #ifndef MINGW | ||
941 | const char *nfds; | ||
942 | unsigned int cnt; | ||
943 | int flags; | ||
944 | #endif | ||
945 | |||
946 | if (GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, "TIMEOUT")) | ||
947 | { | ||
948 | if (GNUNET_OK != | ||
949 | GNUNET_CONFIGURATION_get_value_time (sctx->cfg, sctx->service_name, | ||
950 | "TIMEOUT", &idleout)) | ||
951 | { | ||
952 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
953 | _("Specified value for `%s' of service `%s' is invalid\n"), | ||
954 | "TIMEOUT", sctx->service_name); | ||
955 | return GNUNET_SYSERR; | ||
956 | } | ||
957 | sctx->timeout = idleout; | ||
958 | } | ||
959 | else | ||
960 | sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
961 | |||
962 | if (GNUNET_CONFIGURATION_have_value | ||
963 | (sctx->cfg, sctx->service_name, "TOLERANT")) | ||
964 | { | ||
965 | if (GNUNET_SYSERR == | ||
966 | (tolerant = | ||
967 | GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, | ||
968 | "TOLERANT"))) | ||
969 | { | ||
970 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
971 | _("Specified value for `%s' of service `%s' is invalid\n"), | ||
972 | "TOLERANT", sctx->service_name); | ||
973 | return GNUNET_SYSERR; | ||
974 | } | ||
975 | } | ||
976 | else | ||
977 | tolerant = GNUNET_NO; | ||
978 | |||
979 | #ifndef MINGW | ||
980 | errno = 0; | ||
981 | if ((NULL != (nfds = getenv ("LISTEN_FDS"))) && | ||
982 | (1 == SSCANF (nfds, "%u", &cnt)) && (cnt > 0) && (cnt < FD_SETSIZE) && | ||
983 | (cnt + 4 < FD_SETSIZE)) | ||
984 | { | ||
985 | sctx->lsocks = | ||
986 | GNUNET_malloc (sizeof (struct GNUNET_NETWORK_Handle *) * (cnt + 1)); | ||
987 | while (0 < cnt--) | ||
988 | { | ||
989 | flags = fcntl (3 + cnt, F_GETFD); | ||
990 | if ((flags < 0) || (0 != (flags & FD_CLOEXEC)) || | ||
991 | (NULL == | ||
992 | (sctx->lsocks[cnt] = GNUNET_NETWORK_socket_box_native (3 + cnt)))) | ||
993 | { | ||
994 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
995 | _ | ||
996 | ("Could not access pre-bound socket %u, will try to bind myself\n"), | ||
997 | (unsigned int) 3 + cnt); | ||
998 | cnt++; | ||
999 | while (sctx->lsocks[cnt] != NULL) | ||
1000 | GNUNET_break (0 == GNUNET_NETWORK_socket_close (sctx->lsocks[cnt++])); | ||
1001 | GNUNET_free (sctx->lsocks); | ||
1002 | sctx->lsocks = NULL; | ||
1003 | break; | ||
1004 | } | ||
1005 | } | ||
1006 | unsetenv ("LISTEN_FDS"); | ||
1007 | } | ||
1008 | #else | ||
1009 | if (getenv ("GNUNET_OS_READ_LSOCKS") != NULL) | ||
1010 | { | ||
1011 | receive_sockets_from_parent (sctx); | ||
1012 | putenv ("GNUNET_OS_READ_LSOCKS="); | ||
1013 | } | ||
1014 | #endif | ||
1015 | |||
1016 | if ((NULL == sctx->lsocks) && | ||
1017 | (GNUNET_SYSERR == | ||
1018 | GNUNET_SERVICE_get_server_addresses (sctx->service_name, sctx->cfg, | ||
1019 | &sctx->addrs, &sctx->addrlens))) | ||
1020 | return GNUNET_SYSERR; | ||
1021 | sctx->require_found = tolerant ? GNUNET_NO : GNUNET_YES; | ||
1022 | sctx->match_uid = | ||
1023 | GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, | ||
1024 | "UNIX_MATCH_UID"); | ||
1025 | sctx->match_gid = | ||
1026 | GNUNET_CONFIGURATION_get_value_yesno (sctx->cfg, sctx->service_name, | ||
1027 | "UNIX_MATCH_GID"); | ||
1028 | process_acl4 (&sctx->v4_denied, sctx, "REJECT_FROM"); | ||
1029 | process_acl4 (&sctx->v4_allowed, sctx, "ACCEPT_FROM"); | ||
1030 | process_acl6 (&sctx->v6_denied, sctx, "REJECT_FROM6"); | ||
1031 | process_acl6 (&sctx->v6_allowed, sctx, "ACCEPT_FROM6"); | ||
1032 | |||
1033 | return GNUNET_OK; | ||
1034 | } | ||
1035 | |||
1036 | |||
1037 | /** | ||
1038 | * Get the name of the user that'll be used | ||
1039 | * to provide the service. | ||
1040 | * | ||
1041 | * @param sctx service context | ||
1042 | * @return value of the 'USERNAME' option | ||
1043 | */ | ||
1044 | static char * | ||
1045 | get_user_name (struct GNUNET_SERVICE_Context *sctx) | ||
1046 | { | ||
1047 | char *un; | ||
1048 | |||
1049 | if (GNUNET_OK != | ||
1050 | GNUNET_CONFIGURATION_get_value_filename (sctx->cfg, sctx->service_name, | ||
1051 | "USERNAME", &un)) | ||
1052 | return NULL; | ||
1053 | return un; | ||
1054 | } | ||
1055 | |||
1056 | |||
1057 | /** | ||
1058 | * Write PID file. | ||
1059 | * | ||
1060 | * @param sctx service context | ||
1061 | * @param pid PID to write (should be equal to 'getpid()' | ||
1062 | * @return #GNUNET_OK on success (including no work to be done) | ||
1063 | */ | ||
1064 | static int | ||
1065 | write_pid_file (struct GNUNET_SERVICE_Context *sctx, pid_t pid) | ||
1066 | { | ||
1067 | FILE *pidfd; | ||
1068 | char *pif; | ||
1069 | char *user; | ||
1070 | char *rdir; | ||
1071 | int len; | ||
1072 | |||
1073 | if (NULL == (pif = get_pid_file_name (sctx))) | ||
1074 | return GNUNET_OK; /* no file desired */ | ||
1075 | user = get_user_name (sctx); | ||
1076 | rdir = GNUNET_strdup (pif); | ||
1077 | len = strlen (rdir); | ||
1078 | while ((len > 0) && (rdir[len] != DIR_SEPARATOR)) | ||
1079 | len--; | ||
1080 | rdir[len] = '\0'; | ||
1081 | if (0 != ACCESS (rdir, F_OK)) | ||
1082 | { | ||
1083 | /* we get to create a directory -- and claim it | ||
1084 | * as ours! */ | ||
1085 | (void) GNUNET_DISK_directory_create (rdir); | ||
1086 | if ((NULL != user) && (0 < strlen (user))) | ||
1087 | GNUNET_DISK_file_change_owner (rdir, user); | ||
1088 | } | ||
1089 | if (0 != ACCESS (rdir, W_OK | X_OK)) | ||
1090 | { | ||
1091 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "access", rdir); | ||
1092 | GNUNET_free (rdir); | ||
1093 | GNUNET_free_non_null (user); | ||
1094 | GNUNET_free (pif); | ||
1095 | return GNUNET_SYSERR; | ||
1096 | } | ||
1097 | GNUNET_free (rdir); | ||
1098 | pidfd = FOPEN (pif, "w"); | ||
1099 | if (NULL == pidfd) | ||
1100 | { | ||
1101 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_ERROR, "fopen", pif); | ||
1102 | GNUNET_free (pif); | ||
1103 | GNUNET_free_non_null (user); | ||
1104 | return GNUNET_SYSERR; | ||
1105 | } | ||
1106 | if (0 > FPRINTF (pidfd, "%u", pid)) | ||
1107 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "fprintf", pif); | ||
1108 | GNUNET_break (0 == FCLOSE (pidfd)); | ||
1109 | if ((NULL != user) && (0 < strlen (user))) | ||
1110 | GNUNET_DISK_file_change_owner (pif, user); | ||
1111 | GNUNET_free_non_null (user); | ||
1112 | GNUNET_free (pif); | ||
1113 | return GNUNET_OK; | ||
1114 | } | ||
1115 | |||
1116 | |||
1117 | /** | ||
1118 | * Task run during shutdown. Stops the server/service. | ||
1119 | * | ||
1120 | * @param cls the `struct GNUNET_SERVICE_Context` | ||
1121 | */ | ||
1122 | static void | ||
1123 | shutdown_task (void *cls) | ||
1124 | { | ||
1125 | struct GNUNET_SERVICE_Context *service = cls; | ||
1126 | struct GNUNET_SERVER_Handle *server = service->server; | ||
1127 | |||
1128 | service->shutdown_task = NULL; | ||
1129 | if (0 != (service->options & GNUNET_SERVICE_OPTION_SOFT_SHUTDOWN)) | ||
1130 | GNUNET_SERVER_stop_listening (server); | ||
1131 | else | ||
1132 | GNUNET_SERVER_destroy (server); | ||
1133 | } | ||
1134 | |||
1135 | |||
1136 | /** | ||
1137 | * Initial task for the service. | ||
1138 | * | ||
1139 | * @param cls service context | ||
1140 | */ | ||
1141 | static void | ||
1142 | service_task (void *cls) | ||
1143 | { | ||
1144 | struct GNUNET_SERVICE_Context *sctx = cls; | ||
1145 | unsigned int i; | ||
1146 | |||
1147 | GNUNET_RESOLVER_connect (sctx->cfg); | ||
1148 | if (NULL != sctx->lsocks) | ||
1149 | sctx->server | ||
1150 | = GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks, | ||
1151 | sctx->timeout, sctx->require_found); | ||
1152 | else | ||
1153 | sctx->server | ||
1154 | = GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens, | ||
1155 | sctx->timeout, sctx->require_found); | ||
1156 | if (NULL == sctx->server) | ||
1157 | { | ||
1158 | if (NULL != sctx->addrs) | ||
1159 | for (i = 0; NULL != sctx->addrs[i]; i++) | ||
1160 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
1161 | _("Failed to start `%s' at `%s'\n"), | ||
1162 | sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); | ||
1163 | sctx->ret = GNUNET_SYSERR; | ||
1164 | return; | ||
1165 | } | ||
1166 | #ifndef WINDOWS | ||
1167 | if (NULL != sctx->addrs) | ||
1168 | for (i = 0; NULL != sctx->addrs[i]; i++) | ||
1169 | if ((AF_UNIX == sctx->addrs[i]->sa_family) | ||
1170 | && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0])) | ||
1171 | GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path, | ||
1172 | sctx->match_uid, | ||
1173 | sctx->match_gid); | ||
1174 | #endif | ||
1175 | |||
1176 | |||
1177 | if (0 == (sctx->options & GNUNET_SERVICE_OPTION_MANUAL_SHUTDOWN)) | ||
1178 | { | ||
1179 | /* install a task that will kill the server | ||
1180 | * process if the scheduler ever gets a shutdown signal */ | ||
1181 | sctx->shutdown_task = GNUNET_SCHEDULER_add_shutdown (&shutdown_task, | ||
1182 | sctx); | ||
1183 | } | ||
1184 | sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); | ||
1185 | GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); | ||
1186 | i = 0; | ||
1187 | while (NULL != sctx->my_handlers[i].callback) | ||
1188 | sctx->my_handlers[i++].callback_cls = sctx; | ||
1189 | GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); | ||
1190 | if (-1 != sctx->ready_confirm_fd) | ||
1191 | { | ||
1192 | GNUNET_break (1 == WRITE (sctx->ready_confirm_fd, ".", 1)); | ||
1193 | GNUNET_break (0 == CLOSE (sctx->ready_confirm_fd)); | ||
1194 | sctx->ready_confirm_fd = -1; | ||
1195 | write_pid_file (sctx, getpid ()); | ||
1196 | } | ||
1197 | if (NULL != sctx->addrs) | ||
1198 | { | ||
1199 | i = 0; | ||
1200 | while (NULL != sctx->addrs[i]) | ||
1201 | { | ||
1202 | LOG (GNUNET_ERROR_TYPE_INFO, _("Service `%s' runs at %s\n"), | ||
1203 | sctx->service_name, GNUNET_a2s (sctx->addrs[i], sctx->addrlens[i])); | ||
1204 | i++; | ||
1205 | } | ||
1206 | } | ||
1207 | sctx->task (sctx->task_cls, sctx->server, sctx->cfg); | ||
1208 | } | ||
1209 | |||
1210 | |||
1211 | /** | ||
1212 | * Detach from terminal. | ||
1213 | * | ||
1214 | * @param sctx service context | ||
1215 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
1216 | */ | ||
1217 | static int | ||
1218 | detach_terminal (struct GNUNET_SERVICE_Context *sctx) | ||
1219 | { | ||
1220 | #ifndef MINGW | ||
1221 | pid_t pid; | ||
1222 | int nullfd; | ||
1223 | int filedes[2]; | ||
1224 | |||
1225 | if (0 != PIPE (filedes)) | ||
1226 | { | ||
1227 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "pipe"); | ||
1228 | return GNUNET_SYSERR; | ||
1229 | } | ||
1230 | pid = fork (); | ||
1231 | if (pid < 0) | ||
1232 | { | ||
1233 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "fork"); | ||
1234 | return GNUNET_SYSERR; | ||
1235 | } | ||
1236 | if (0 != pid) | ||
1237 | { | ||
1238 | /* Parent */ | ||
1239 | char c; | ||
1240 | |||
1241 | GNUNET_break (0 == CLOSE (filedes[1])); | ||
1242 | c = 'X'; | ||
1243 | if (1 != READ (filedes[0], &c, sizeof (char))) | ||
1244 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "read"); | ||
1245 | fflush (stdout); | ||
1246 | switch (c) | ||
1247 | { | ||
1248 | case '.': | ||
1249 | exit (0); | ||
1250 | case 'I': | ||
1251 | LOG (GNUNET_ERROR_TYPE_INFO, _("Service process failed to initialize\n")); | ||
1252 | break; | ||
1253 | case 'S': | ||
1254 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
1255 | _("Service process could not initialize server function\n")); | ||
1256 | break; | ||
1257 | case 'X': | ||
1258 | LOG (GNUNET_ERROR_TYPE_INFO, | ||
1259 | _("Service process failed to report status\n")); | ||
1260 | break; | ||
1261 | } | ||
1262 | exit (1); /* child reported error */ | ||
1263 | } | ||
1264 | GNUNET_break (0 == CLOSE (0)); | ||
1265 | GNUNET_break (0 == CLOSE (1)); | ||
1266 | GNUNET_break (0 == CLOSE (filedes[0])); | ||
1267 | nullfd = OPEN ("/dev/null", O_RDWR | O_APPEND); | ||
1268 | if (nullfd < 0) | ||
1269 | return GNUNET_SYSERR; | ||
1270 | /* set stdin/stdout to /dev/null */ | ||
1271 | if ((dup2 (nullfd, 0) < 0) || (dup2 (nullfd, 1) < 0)) | ||
1272 | { | ||
1273 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "dup2"); | ||
1274 | (void) CLOSE (nullfd); | ||
1275 | return GNUNET_SYSERR; | ||
1276 | } | ||
1277 | (void) CLOSE (nullfd); | ||
1278 | /* Detach from controlling terminal */ | ||
1279 | pid = setsid (); | ||
1280 | if (-1 == pid) | ||
1281 | LOG_STRERROR (GNUNET_ERROR_TYPE_ERROR, "setsid"); | ||
1282 | sctx->ready_confirm_fd = filedes[1]; | ||
1283 | #else | ||
1284 | /* FIXME: we probably need to do something else | ||
1285 | * elsewhere in order to fork the process itself... */ | ||
1286 | FreeConsole (); | ||
1287 | #endif | ||
1288 | return GNUNET_OK; | ||
1289 | } | ||
1290 | |||
1291 | |||
1292 | /** | ||
1293 | * Set user ID. | ||
1294 | * | ||
1295 | * @param sctx service context | ||
1296 | * @return #GNUNET_OK on success, #GNUNET_SYSERR on error | ||
1297 | */ | ||
1298 | static int | ||
1299 | set_user_id (struct GNUNET_SERVICE_Context *sctx) | ||
1300 | { | ||
1301 | char *user; | ||
1302 | |||
1303 | if (NULL == (user = get_user_name (sctx))) | ||
1304 | return GNUNET_OK; /* keep */ | ||
1305 | #ifndef MINGW | ||
1306 | struct passwd *pws; | ||
1307 | |||
1308 | errno = 0; | ||
1309 | pws = getpwnam (user); | ||
1310 | if (NULL == pws) | ||
1311 | { | ||
1312 | LOG (GNUNET_ERROR_TYPE_ERROR, | ||
1313 | _("Cannot obtain information about user `%s': %s\n"), user, | ||
1314 | errno == 0 ? _("No such user") : STRERROR (errno)); | ||
1315 | GNUNET_free (user); | ||
1316 | return GNUNET_SYSERR; | ||
1317 | } | ||
1318 | if ((0 != setgid (pws->pw_gid)) || (0 != setegid (pws->pw_gid)) || | ||
1319 | #if HAVE_INITGROUPS | ||
1320 | (0 != initgroups (user, pws->pw_gid)) || | ||
1321 | #endif | ||
1322 | (0 != setuid (pws->pw_uid)) || (0 != seteuid (pws->pw_uid))) | ||
1323 | { | ||
1324 | if ((0 != setregid (pws->pw_gid, pws->pw_gid)) || | ||
1325 | (0 != setreuid (pws->pw_uid, pws->pw_uid))) | ||
1326 | { | ||
1327 | LOG (GNUNET_ERROR_TYPE_ERROR, _("Cannot change user/group to `%s': %s\n"), | ||
1328 | user, STRERROR (errno)); | ||
1329 | GNUNET_free (user); | ||
1330 | return GNUNET_SYSERR; | ||
1331 | } | ||
1332 | } | ||
1333 | #endif | ||
1334 | GNUNET_free (user); | ||
1335 | return GNUNET_OK; | ||
1336 | } | ||
1337 | |||
1338 | |||
1339 | /** | ||
1340 | * Delete the PID file that was created by our parent. | ||
1341 | * | ||
1342 | * @param sctx service context | ||
1343 | */ | ||
1344 | static void | ||
1345 | pid_file_delete (struct GNUNET_SERVICE_Context *sctx) | ||
1346 | { | ||
1347 | char *pif = get_pid_file_name (sctx); | ||
1348 | |||
1349 | if (NULL == pif) | ||
1350 | return; /* no PID file */ | ||
1351 | if (0 != UNLINK (pif)) | ||
1352 | LOG_STRERROR_FILE (GNUNET_ERROR_TYPE_WARNING, "unlink", pif); | ||
1353 | GNUNET_free (pif); | ||
1354 | } | ||
1355 | |||
1356 | |||
1357 | /** | ||
1358 | * Run a standard GNUnet service startup sequence (initialize loggers | ||
1359 | * and configuration, parse options). | ||
1360 | * | ||
1361 | * @param argc number of command line arguments | ||
1362 | * @param argv command line arguments | ||
1363 | * @param service_name our service name | ||
1364 | * @param options service options | ||
1365 | * @param task main task of the service | ||
1366 | * @param task_cls closure for @a task | ||
1367 | * @return #GNUNET_SYSERR on error, #GNUNET_OK | ||
1368 | * if we shutdown nicely | ||
1369 | */ | ||
1370 | int | ||
1371 | GNUNET_SERVICE_run (int argc, char *const *argv, | ||
1372 | const char *service_name, | ||
1373 | enum GNUNET_SERVICE_Options options, | ||
1374 | GNUNET_SERVICE_Main task, | ||
1375 | void *task_cls) | ||
1376 | { | ||
1377 | #define HANDLE_ERROR do { GNUNET_break (0); goto shutdown; } while (0) | ||
1378 | |||
1379 | int err; | ||
1380 | int ret; | ||
1381 | char *cfg_fn; | ||
1382 | char *opt_cfg_fn; | ||
1383 | char *loglev; | ||
1384 | char *logfile; | ||
1385 | int do_daemonize; | ||
1386 | unsigned int i; | ||
1387 | unsigned long long skew_offset; | ||
1388 | unsigned long long skew_variance; | ||
1389 | long long clock_offset; | ||
1390 | struct GNUNET_SERVICE_Context sctx; | ||
1391 | struct GNUNET_CONFIGURATION_Handle *cfg; | ||
1392 | const char *xdg; | ||
1393 | |||
1394 | struct GNUNET_GETOPT_CommandLineOption service_options[] = { | ||
1395 | GNUNET_GETOPT_OPTION_CFG_FILE (&opt_cfg_fn), | ||
1396 | GNUNET_GETOPT_OPTION_SET_ONE ('d', | ||
1397 | "daemonize", | ||
1398 | gettext_noop ("do daemonize (detach from terminal)"), | ||
1399 | &do_daemonize), | ||
1400 | GNUNET_GETOPT_OPTION_HELP (NULL), | ||
1401 | GNUNET_GETOPT_OPTION_LOGLEVEL (&loglev), | ||
1402 | GNUNET_GETOPT_OPTION_LOGFILE (&logfile), | ||
1403 | GNUNET_GETOPT_OPTION_VERSION (PACKAGE_VERSION " " VCS_VERSION), | ||
1404 | GNUNET_GETOPT_OPTION_END | ||
1405 | }; | ||
1406 | err = 1; | ||
1407 | do_daemonize = 0; | ||
1408 | logfile = NULL; | ||
1409 | loglev = NULL; | ||
1410 | opt_cfg_fn = NULL; | ||
1411 | xdg = getenv ("XDG_CONFIG_HOME"); | ||
1412 | if (NULL != xdg) | ||
1413 | GNUNET_asprintf (&cfg_fn, | ||
1414 | "%s%s%s", | ||
1415 | xdg, | ||
1416 | DIR_SEPARATOR_STR, | ||
1417 | GNUNET_OS_project_data_get ()->config_file); | ||
1418 | else | ||
1419 | cfg_fn = GNUNET_strdup (GNUNET_OS_project_data_get ()->user_config_file); | ||
1420 | memset (&sctx, 0, sizeof (sctx)); | ||
1421 | sctx.options = options; | ||
1422 | sctx.ready_confirm_fd = -1; | ||
1423 | sctx.ret = GNUNET_OK; | ||
1424 | sctx.timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
1425 | sctx.task = task; | ||
1426 | sctx.task_cls = task_cls; | ||
1427 | sctx.service_name = service_name; | ||
1428 | sctx.cfg = cfg = GNUNET_CONFIGURATION_create (); | ||
1429 | |||
1430 | /* setup subsystems */ | ||
1431 | ret = GNUNET_GETOPT_run (service_name, service_options, argc, argv); | ||
1432 | if (GNUNET_SYSERR == ret) | ||
1433 | goto shutdown; | ||
1434 | if (GNUNET_NO == ret) | ||
1435 | { | ||
1436 | err = 0; | ||
1437 | goto shutdown; | ||
1438 | } | ||
1439 | if (GNUNET_OK != GNUNET_log_setup (service_name, loglev, logfile)) | ||
1440 | HANDLE_ERROR; | ||
1441 | if (NULL == opt_cfg_fn) | ||
1442 | opt_cfg_fn = GNUNET_strdup (cfg_fn); | ||
1443 | if (GNUNET_YES == GNUNET_DISK_file_test (opt_cfg_fn)) | ||
1444 | { | ||
1445 | if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, opt_cfg_fn)) | ||
1446 | { | ||
1447 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1448 | _("Malformed configuration file `%s', exit ...\n"), | ||
1449 | opt_cfg_fn); | ||
1450 | goto shutdown; | ||
1451 | } | ||
1452 | } | ||
1453 | else | ||
1454 | { | ||
1455 | if (GNUNET_SYSERR == GNUNET_CONFIGURATION_load (cfg, NULL)) | ||
1456 | { | ||
1457 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1458 | _("Malformed configuration, exit ...\n")); | ||
1459 | goto shutdown; | ||
1460 | } | ||
1461 | if (0 != strcmp (opt_cfg_fn, cfg_fn)) | ||
1462 | GNUNET_log (GNUNET_ERROR_TYPE_ERROR, | ||
1463 | _("Could not access configuration file `%s'\n"), | ||
1464 | opt_cfg_fn); | ||
1465 | } | ||
1466 | if (GNUNET_OK != setup_service (&sctx)) | ||
1467 | goto shutdown; | ||
1468 | if ((1 == do_daemonize) && (GNUNET_OK != detach_terminal (&sctx))) | ||
1469 | HANDLE_ERROR; | ||
1470 | if (GNUNET_OK != set_user_id (&sctx)) | ||
1471 | goto shutdown; | ||
1472 | LOG (GNUNET_ERROR_TYPE_DEBUG, | ||
1473 | "Service `%s' runs with configuration from `%s'\n", | ||
1474 | service_name, | ||
1475 | opt_cfg_fn); | ||
1476 | if ((GNUNET_OK == | ||
1477 | GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING", | ||
1478 | "SKEW_OFFSET", &skew_offset)) && | ||
1479 | (GNUNET_OK == | ||
1480 | GNUNET_CONFIGURATION_get_value_number (sctx.cfg, "TESTING", | ||
1481 | "SKEW_VARIANCE", &skew_variance))) | ||
1482 | { | ||
1483 | clock_offset = skew_offset - skew_variance; | ||
1484 | GNUNET_TIME_set_offset (clock_offset); | ||
1485 | LOG (GNUNET_ERROR_TYPE_DEBUG, "Skewing clock by %dll ms\n", clock_offset); | ||
1486 | } | ||
1487 | /* actually run service */ | ||
1488 | err = 0; | ||
1489 | GNUNET_SCHEDULER_run (&service_task, &sctx); | ||
1490 | /* shutdown */ | ||
1491 | if ((1 == do_daemonize) && (NULL != sctx.server)) | ||
1492 | pid_file_delete (&sctx); | ||
1493 | GNUNET_free_non_null (sctx.my_handlers); | ||
1494 | |||
1495 | shutdown: | ||
1496 | if (-1 != sctx.ready_confirm_fd) | ||
1497 | { | ||
1498 | if (1 != WRITE (sctx.ready_confirm_fd, err ? "I" : "S", 1)) | ||
1499 | LOG_STRERROR (GNUNET_ERROR_TYPE_WARNING, "write"); | ||
1500 | GNUNET_break (0 == CLOSE (sctx.ready_confirm_fd)); | ||
1501 | } | ||
1502 | #if HAVE_MALLINFO | ||
1503 | { | ||
1504 | char *counter; | ||
1505 | |||
1506 | if ( (GNUNET_YES == | ||
1507 | GNUNET_CONFIGURATION_have_value (sctx.cfg, service_name, | ||
1508 | "GAUGER_HEAP")) && | ||
1509 | (GNUNET_OK == | ||
1510 | GNUNET_CONFIGURATION_get_value_string (sctx.cfg, service_name, | ||
1511 | "GAUGER_HEAP", | ||
1512 | &counter)) ) | ||
1513 | { | ||
1514 | struct mallinfo mi; | ||
1515 | |||
1516 | mi = mallinfo (); | ||
1517 | GAUGER (service_name, counter, mi.usmblks, "blocks"); | ||
1518 | GNUNET_free (counter); | ||
1519 | } | ||
1520 | } | ||
1521 | #endif | ||
1522 | GNUNET_CONFIGURATION_destroy (cfg); | ||
1523 | i = 0; | ||
1524 | if (NULL != sctx.addrs) | ||
1525 | while (NULL != sctx.addrs[i]) | ||
1526 | GNUNET_free (sctx.addrs[i++]); | ||
1527 | GNUNET_free_non_null (sctx.addrs); | ||
1528 | GNUNET_free_non_null (sctx.addrlens); | ||
1529 | GNUNET_free_non_null (logfile); | ||
1530 | GNUNET_free_non_null (loglev); | ||
1531 | GNUNET_free (cfg_fn); | ||
1532 | GNUNET_free_non_null (opt_cfg_fn); | ||
1533 | GNUNET_free_non_null (sctx.v4_denied); | ||
1534 | GNUNET_free_non_null (sctx.v6_denied); | ||
1535 | GNUNET_free_non_null (sctx.v4_allowed); | ||
1536 | GNUNET_free_non_null (sctx.v6_allowed); | ||
1537 | |||
1538 | return err ? GNUNET_SYSERR : sctx.ret; | ||
1539 | } | ||
1540 | |||
1541 | |||
1542 | /** | ||
1543 | * Run a service startup sequence within an existing | ||
1544 | * initialized system. | ||
1545 | * | ||
1546 | * @param service_name our service name | ||
1547 | * @param cfg configuration to use | ||
1548 | * @param options service options | ||
1549 | * @return NULL on error, service handle | ||
1550 | */ | ||
1551 | struct GNUNET_SERVICE_Context * | ||
1552 | GNUNET_SERVICE_start (const char *service_name, | ||
1553 | const struct GNUNET_CONFIGURATION_Handle *cfg, | ||
1554 | enum GNUNET_SERVICE_Options options) | ||
1555 | { | ||
1556 | int i; | ||
1557 | struct GNUNET_SERVICE_Context *sctx; | ||
1558 | |||
1559 | sctx = GNUNET_new (struct GNUNET_SERVICE_Context); | ||
1560 | sctx->ready_confirm_fd = -1; /* no daemonizing */ | ||
1561 | sctx->ret = GNUNET_OK; | ||
1562 | sctx->timeout = GNUNET_TIME_UNIT_FOREVER_REL; | ||
1563 | sctx->service_name = service_name; | ||
1564 | sctx->cfg = cfg; | ||
1565 | sctx->options = options; | ||
1566 | |||
1567 | /* setup subsystems */ | ||
1568 | if (GNUNET_OK != setup_service (sctx)) | ||
1569 | { | ||
1570 | GNUNET_SERVICE_stop (sctx); | ||
1571 | return NULL; | ||
1572 | } | ||
1573 | if (NULL != sctx->lsocks) | ||
1574 | sctx->server = | ||
1575 | GNUNET_SERVER_create_with_sockets (&check_access, sctx, sctx->lsocks, | ||
1576 | sctx->timeout, sctx->require_found); | ||
1577 | else | ||
1578 | sctx->server = | ||
1579 | GNUNET_SERVER_create (&check_access, sctx, sctx->addrs, sctx->addrlens, | ||
1580 | sctx->timeout, sctx->require_found); | ||
1581 | |||
1582 | if (NULL == sctx->server) | ||
1583 | { | ||
1584 | GNUNET_SERVICE_stop (sctx); | ||
1585 | return NULL; | ||
1586 | } | ||
1587 | #ifndef WINDOWS | ||
1588 | if (NULL != sctx->addrs) | ||
1589 | for (i = 0; NULL != sctx->addrs[i]; i++) | ||
1590 | if ((AF_UNIX == sctx->addrs[i]->sa_family) | ||
1591 | && ('\0' != ((const struct sockaddr_un *)sctx->addrs[i])->sun_path[0])) | ||
1592 | GNUNET_DISK_fix_permissions (((const struct sockaddr_un *)sctx->addrs[i])->sun_path, | ||
1593 | sctx->match_uid, | ||
1594 | sctx->match_gid); | ||
1595 | #endif | ||
1596 | sctx->my_handlers = GNUNET_malloc (sizeof (defhandlers)); | ||
1597 | GNUNET_memcpy (sctx->my_handlers, defhandlers, sizeof (defhandlers)); | ||
1598 | i = 0; | ||
1599 | while ((sctx->my_handlers[i].callback != NULL)) | ||
1600 | sctx->my_handlers[i++].callback_cls = sctx; | ||
1601 | GNUNET_SERVER_add_handlers (sctx->server, sctx->my_handlers); | ||
1602 | return sctx; | ||
1603 | } | ||
1604 | |||
1605 | |||
1606 | /** | ||
1607 | * Obtain the server used by a service. Note that the server must NOT | ||
1608 | * be destroyed by the caller. | ||
1609 | * | ||
1610 | * @param ctx the service context returned from the start function | ||
1611 | * @return handle to the server for this service, NULL if there is none | ||
1612 | */ | ||
1613 | struct GNUNET_SERVER_Handle * | ||
1614 | GNUNET_SERVICE_get_server (struct GNUNET_SERVICE_Context *ctx) | ||
1615 | { | ||
1616 | return ctx->server; | ||
1617 | } | ||
1618 | |||
1619 | |||
1620 | /** | ||
1621 | * Get the NULL-terminated array of listen sockets for this service. | ||
1622 | * | ||
1623 | * @param ctx service context to query | ||
1624 | * @return NULL if there are no listen sockets, otherwise NULL-terminated | ||
1625 | * array of listen sockets. | ||
1626 | */ | ||
1627 | struct GNUNET_NETWORK_Handle *const* | ||
1628 | GNUNET_SERVICE_get_listen_sockets (struct GNUNET_SERVICE_Context *ctx) | ||
1629 | { | ||
1630 | return ctx->lsocks; | ||
1631 | } | ||
1632 | |||
1633 | |||
1634 | /** | ||
1635 | * Stop a service that was started with "GNUNET_SERVICE_start". | ||
1636 | * | ||
1637 | * @param sctx the service context returned from the start function | ||
1638 | */ | ||
1639 | void | ||
1640 | GNUNET_SERVICE_stop (struct GNUNET_SERVICE_Context *sctx) | ||
1641 | { | ||
1642 | unsigned int i; | ||
1643 | |||
1644 | #if HAVE_MALLINFO | ||
1645 | { | ||
1646 | char *counter; | ||
1647 | |||
1648 | if ( (GNUNET_YES == | ||
1649 | GNUNET_CONFIGURATION_have_value (sctx->cfg, sctx->service_name, | ||
1650 | "GAUGER_HEAP")) && | ||
1651 | (GNUNET_OK == | ||
1652 | GNUNET_CONFIGURATION_get_value_string (sctx->cfg, sctx->service_name, | ||
1653 | "GAUGER_HEAP", | ||
1654 | &counter)) ) | ||
1655 | { | ||
1656 | struct mallinfo mi; | ||
1657 | |||
1658 | mi = mallinfo (); | ||
1659 | GAUGER (sctx->service_name, counter, mi.usmblks, "blocks"); | ||
1660 | GNUNET_free (counter); | ||
1661 | } | ||
1662 | } | ||
1663 | #endif | ||
1664 | if (NULL != sctx->shutdown_task) | ||
1665 | { | ||
1666 | GNUNET_SCHEDULER_cancel (sctx->shutdown_task); | ||
1667 | sctx->shutdown_task = NULL; | ||
1668 | } | ||
1669 | if (NULL != sctx->server) | ||
1670 | GNUNET_SERVER_destroy (sctx->server); | ||
1671 | GNUNET_free_non_null (sctx->my_handlers); | ||
1672 | if (NULL != sctx->addrs) | ||
1673 | { | ||
1674 | i = 0; | ||
1675 | while (NULL != sctx->addrs[i]) | ||
1676 | GNUNET_free (sctx->addrs[i++]); | ||
1677 | GNUNET_free (sctx->addrs); | ||
1678 | } | ||
1679 | GNUNET_free_non_null (sctx->addrlens); | ||
1680 | GNUNET_free_non_null (sctx->v4_denied); | ||
1681 | GNUNET_free_non_null (sctx->v6_denied); | ||
1682 | GNUNET_free_non_null (sctx->v4_allowed); | ||
1683 | GNUNET_free_non_null (sctx->v6_allowed); | ||
1684 | GNUNET_free (sctx); | ||
1685 | } | ||
1686 | |||
1687 | |||
1688 | /* end of service.c */ | ||