Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : /* FIXME: "posix" files shouldn't be depending on _GNU_SOURCE */
35 : #ifndef _GNU_SOURCE
36 : #define _GNU_SOURCE
37 : #endif
38 :
39 : #include <grpc/support/port_platform.h>
40 :
41 : #ifdef GPR_POSIX_SOCKET
42 :
43 : #include "src/core/iomgr/tcp_server.h"
44 :
45 : #include <errno.h>
46 : #include <fcntl.h>
47 : #include <limits.h>
48 : #include <netinet/in.h>
49 : #include <netinet/tcp.h>
50 : #include <stdio.h>
51 : #include <string.h>
52 : #include <sys/socket.h>
53 : #include <sys/stat.h>
54 : #include <sys/types.h>
55 : #include <sys/un.h>
56 : #include <unistd.h>
57 :
58 : #include "src/core/iomgr/pollset_posix.h"
59 : #include "src/core/iomgr/resolve_address.h"
60 : #include "src/core/iomgr/sockaddr_utils.h"
61 : #include "src/core/iomgr/socket_utils_posix.h"
62 : #include "src/core/iomgr/tcp_posix.h"
63 : #include "src/core/support/string.h"
64 : #include <grpc/support/alloc.h>
65 : #include <grpc/support/log.h>
66 : #include <grpc/support/string_util.h>
67 : #include <grpc/support/sync.h>
68 : #include <grpc/support/time.h>
69 :
70 : #define MIN_SAFE_ACCEPT_QUEUE_SIZE 100
71 :
72 : static gpr_once s_init_max_accept_queue_size;
73 : static int s_max_accept_queue_size;
74 :
75 : /* one listening port */
76 : struct grpc_tcp_listener {
77 : int fd;
78 : grpc_fd *emfd;
79 : grpc_tcp_server *server;
80 : union {
81 : gpr_uint8 untyped[GRPC_MAX_SOCKADDR_SIZE];
82 : struct sockaddr sockaddr;
83 : struct sockaddr_un un;
84 : } addr;
85 : size_t addr_len;
86 : int port;
87 : grpc_closure read_closure;
88 : grpc_closure destroyed_closure;
89 : gpr_refcount refs;
90 : struct grpc_tcp_listener *next;
91 : /* When we add a listener, more than one can be created, mainly because of
92 : IPv6. A sibling will still be in the normal list, but will be flagged
93 : as such. Any action, such as ref or unref, will affect all of the
94 : siblings in the list. */
95 : struct grpc_tcp_listener *sibling;
96 : int is_sibling;
97 : };
98 :
99 992 : static void unlink_if_unix_domain_socket(const struct sockaddr_un *un) {
100 : struct stat st;
101 :
102 992 : if (stat(un->sun_path, &st) == 0 && (st.st_mode & S_IFMT) == S_IFSOCK) {
103 496 : unlink(un->sun_path);
104 : }
105 992 : }
106 :
107 : /* the overall server */
108 : struct grpc_tcp_server {
109 : /* Called whenever accept() succeeds on a server port. */
110 : grpc_tcp_server_cb on_accept_cb;
111 : void *on_accept_cb_arg;
112 :
113 : gpr_mu mu;
114 :
115 : /* active port count: how many ports are actually still listening */
116 : size_t active_ports;
117 : /* destroyed port count: how many ports are completely destroyed */
118 : size_t destroyed_ports;
119 :
120 : /* is this server shutting down? (boolean) */
121 : int shutdown;
122 :
123 : /* linked list of server ports */
124 : grpc_tcp_listener *head;
125 : unsigned nports;
126 :
127 : /* shutdown callback */
128 : grpc_closure *shutdown_complete;
129 :
130 : /* all pollsets interested in new connections */
131 : grpc_pollset **pollsets;
132 : /* number of pollsets in the pollsets array */
133 : size_t pollset_count;
134 : };
135 :
136 2764 : grpc_tcp_server *grpc_tcp_server_create(void) {
137 2764 : grpc_tcp_server *s = gpr_malloc(sizeof(grpc_tcp_server));
138 2764 : gpr_mu_init(&s->mu);
139 2764 : s->active_ports = 0;
140 2764 : s->destroyed_ports = 0;
141 2764 : s->shutdown = 0;
142 2764 : s->on_accept_cb = NULL;
143 2764 : s->on_accept_cb_arg = NULL;
144 2764 : s->head = NULL;
145 2764 : s->nports = 0;
146 2764 : return s;
147 : }
148 :
149 2762 : static void finish_shutdown(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
150 2762 : grpc_exec_ctx_enqueue(exec_ctx, s->shutdown_complete, 1);
151 :
152 2762 : gpr_mu_destroy(&s->mu);
153 :
154 10082 : while (s->head) {
155 4513 : grpc_tcp_listener *sp = s->head;
156 4558 : s->head = sp->next;
157 4558 : grpc_tcp_listener_unref(sp);
158 : }
159 :
160 2762 : gpr_free(s);
161 2762 : }
162 :
163 4558 : static void destroyed_port(grpc_exec_ctx *exec_ctx, void *server, int success) {
164 4513 : grpc_tcp_server *s = server;
165 4558 : gpr_mu_lock(&s->mu);
166 4558 : s->destroyed_ports++;
167 4558 : if (s->destroyed_ports == s->nports) {
168 2760 : gpr_mu_unlock(&s->mu);
169 2760 : finish_shutdown(exec_ctx, s);
170 : } else {
171 1798 : GPR_ASSERT(s->destroyed_ports < s->nports);
172 1798 : gpr_mu_unlock(&s->mu);
173 : }
174 4558 : }
175 :
176 : /* called when all listening endpoints have been shutdown, so no further
177 : events will be received on them - at this point it's safe to destroy
178 : things */
179 2762 : static void deactivated_all_ports(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s) {
180 : /* delete ALL the things */
181 2762 : gpr_mu_lock(&s->mu);
182 :
183 2762 : if (!s->shutdown) {
184 0 : gpr_mu_unlock(&s->mu);
185 2762 : return;
186 : }
187 :
188 2762 : if (s->head) {
189 : grpc_tcp_listener *sp;
190 7290 : for (sp = s->head; sp; sp = sp->next) {
191 4558 : if (sp->addr.sockaddr.sa_family == AF_UNIX) {
192 496 : unlink_if_unix_domain_socket(&sp->addr.un);
193 : }
194 4558 : sp->destroyed_closure.cb = destroyed_port;
195 4558 : sp->destroyed_closure.cb_arg = s;
196 4558 : grpc_fd_orphan(exec_ctx, sp->emfd, &sp->destroyed_closure, NULL,
197 : "tcp_listener_shutdown");
198 : }
199 2760 : gpr_mu_unlock(&s->mu);
200 : } else {
201 2 : gpr_mu_unlock(&s->mu);
202 2 : finish_shutdown(exec_ctx, s);
203 : }
204 : }
205 :
206 2762 : void grpc_tcp_server_destroy(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
207 : grpc_closure *closure) {
208 2762 : gpr_mu_lock(&s->mu);
209 :
210 2762 : GPR_ASSERT(!s->shutdown);
211 2762 : s->shutdown = 1;
212 :
213 2762 : s->shutdown_complete = closure;
214 :
215 : /* shutdown all fd's */
216 2762 : if (s->active_ports) {
217 : grpc_tcp_listener *sp;
218 7314 : for (sp = s->head; sp; sp = sp->next) {
219 4556 : grpc_fd_shutdown(exec_ctx, sp->emfd);
220 : }
221 2758 : gpr_mu_unlock(&s->mu);
222 : } else {
223 4 : gpr_mu_unlock(&s->mu);
224 4 : deactivated_all_ports(exec_ctx, s);
225 : }
226 2762 : }
227 :
228 : /* get max listen queue size on linux */
229 637 : static void init_max_accept_queue_size(void) {
230 636 : int n = SOMAXCONN;
231 : char buf[64];
232 637 : FILE *fp = fopen("/proc/sys/net/core/somaxconn", "r");
233 637 : if (fp == NULL) {
234 : /* 2.4 kernel. */
235 0 : s_max_accept_queue_size = SOMAXCONN;
236 637 : return;
237 : }
238 637 : if (fgets(buf, sizeof buf, fp)) {
239 : char *end;
240 637 : long i = strtol(buf, &end, 10);
241 637 : if (i > 0 && i <= INT_MAX && end && *end == 0) {
242 0 : n = (int)i;
243 : }
244 : }
245 637 : fclose(fp);
246 637 : s_max_accept_queue_size = n;
247 :
248 637 : if (s_max_accept_queue_size < MIN_SAFE_ACCEPT_QUEUE_SIZE) {
249 0 : gpr_log(GPR_INFO,
250 : "Suspiciously small accept queue (%d) will probably lead to "
251 : "connection drops",
252 : s_max_accept_queue_size);
253 : }
254 : }
255 :
256 4513 : static int get_max_accept_queue_size(void) {
257 4560 : gpr_once_init(&s_init_max_accept_queue_size, init_max_accept_queue_size);
258 4560 : return s_max_accept_queue_size;
259 : }
260 :
261 : /* Prepare a recently-created socket for listening. */
262 4560 : static int prepare_socket(int fd, const struct sockaddr *addr,
263 : size_t addr_len) {
264 : struct sockaddr_storage sockname_temp;
265 : socklen_t sockname_len;
266 :
267 4560 : if (fd < 0) {
268 0 : goto error;
269 : }
270 :
271 9120 : if (!grpc_set_socket_nonblocking(fd, 1) || !grpc_set_socket_cloexec(fd, 1) ||
272 12688 : (addr->sa_family != AF_UNIX && (!grpc_set_socket_low_latency(fd, 1) ||
273 8624 : !grpc_set_socket_reuse_addr(fd, 1))) ||
274 4560 : !grpc_set_socket_no_sigpipe_if_possible(fd)) {
275 0 : gpr_log(GPR_ERROR, "Unable to configure socket %d: %s", fd,
276 0 : strerror(errno));
277 0 : goto error;
278 : }
279 :
280 4560 : GPR_ASSERT(addr_len < ~(socklen_t)0);
281 4560 : if (bind(fd, addr, (socklen_t)addr_len) < 0) {
282 : char *addr_str;
283 0 : grpc_sockaddr_to_string(&addr_str, addr, 0);
284 0 : gpr_log(GPR_ERROR, "bind addr=%s: %s", addr_str, strerror(errno));
285 0 : gpr_free(addr_str);
286 0 : goto error;
287 : }
288 :
289 4560 : if (listen(fd, get_max_accept_queue_size()) < 0) {
290 0 : gpr_log(GPR_ERROR, "listen: %s", strerror(errno));
291 0 : goto error;
292 : }
293 :
294 4560 : sockname_len = sizeof(sockname_temp);
295 4560 : if (getsockname(fd, (struct sockaddr *)&sockname_temp, &sockname_len) < 0) {
296 0 : goto error;
297 : }
298 :
299 4560 : return grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
300 :
301 : error:
302 0 : if (fd >= 0) {
303 0 : close(fd);
304 : }
305 0 : return -1;
306 : }
307 :
308 : /* event manager callback when reads are ready */
309 6878 : static void on_read(grpc_exec_ctx *exec_ctx, void *arg, int success) {
310 6792 : grpc_tcp_listener *sp = arg;
311 : grpc_fd *fdobj;
312 : size_t i;
313 :
314 6878 : if (!success) {
315 4511 : goto error;
316 : }
317 :
318 : /* loop until accept4 returns EAGAIN, and then re-arm notification */
319 : for (;;) {
320 : struct sockaddr_storage addr;
321 4648 : socklen_t addrlen = sizeof(addr);
322 : char *addr_str;
323 : char *name;
324 : /* Note: If we ever decide to return this address to the user, remember to
325 : strip off the ::ffff:0.0.0.0/96 prefix first. */
326 4648 : int fd = grpc_accept4(sp->fd, (struct sockaddr *)&addr, &addrlen, 1, 1);
327 4648 : if (fd < 0) {
328 2322 : switch (errno) {
329 : case EINTR:
330 0 : continue;
331 : case EAGAIN:
332 2322 : grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
333 9200 : return;
334 : default:
335 0 : gpr_log(GPR_ERROR, "Failed accept4: %s", strerror(errno));
336 0 : goto error;
337 : }
338 : }
339 :
340 2326 : grpc_set_socket_no_sigpipe_if_possible(fd);
341 :
342 2326 : addr_str = grpc_sockaddr_to_uri((struct sockaddr *)&addr);
343 2326 : gpr_asprintf(&name, "tcp-server-connection:%s", addr_str);
344 :
345 2326 : if (grpc_tcp_trace) {
346 0 : gpr_log(GPR_DEBUG, "SERVER_CONNECT: incoming connection: %s", addr_str);
347 : }
348 :
349 2326 : fdobj = grpc_fd_create(fd, name);
350 : /* TODO(ctiller): revise this when we have server-side sharding
351 : of channels -- we certainly should not be automatically adding every
352 : incoming channel to every pollset owned by the server */
353 4788 : for (i = 0; i < sp->server->pollset_count; i++) {
354 2462 : grpc_pollset_add_fd(exec_ctx, sp->server->pollsets[i], fdobj);
355 : }
356 4652 : sp->server->on_accept_cb(
357 2326 : exec_ctx, sp->server->on_accept_cb_arg,
358 : grpc_tcp_create(fdobj, GRPC_TCP_DEFAULT_READ_SLICE_SIZE, addr_str));
359 :
360 2326 : gpr_free(name);
361 2326 : gpr_free(addr_str);
362 2285 : }
363 :
364 : GPR_UNREACHABLE_CODE(return );
365 :
366 : error:
367 4556 : gpr_mu_lock(&sp->server->mu);
368 4556 : if (0 == --sp->server->active_ports) {
369 2758 : gpr_mu_unlock(&sp->server->mu);
370 2758 : deactivated_all_ports(exec_ctx, sp->server);
371 : } else {
372 1798 : gpr_mu_unlock(&sp->server->mu);
373 : }
374 : }
375 :
376 4560 : static grpc_tcp_listener *add_socket_to_server(grpc_tcp_server *s, int fd,
377 : const struct sockaddr *addr,
378 : size_t addr_len) {
379 4513 : grpc_tcp_listener *sp = NULL;
380 : int port;
381 : char *addr_str;
382 : char *name;
383 :
384 4560 : port = prepare_socket(fd, addr, addr_len);
385 4560 : if (port >= 0) {
386 4560 : grpc_sockaddr_to_string(&addr_str, (struct sockaddr *)&addr, 1);
387 4560 : gpr_asprintf(&name, "tcp-server-listener:%s", addr_str);
388 4560 : gpr_mu_lock(&s->mu);
389 4560 : s->nports++;
390 4560 : GPR_ASSERT(!s->on_accept_cb && "must add ports before starting server");
391 4560 : sp = gpr_malloc(sizeof(grpc_tcp_listener));
392 4560 : sp->next = s->head;
393 4560 : s->head = sp;
394 4560 : sp->server = s;
395 4560 : sp->fd = fd;
396 4560 : sp->emfd = grpc_fd_create(fd, name);
397 4560 : memcpy(sp->addr.untyped, addr, addr_len);
398 4560 : sp->addr_len = addr_len;
399 4560 : sp->port = port;
400 4560 : sp->is_sibling = 0;
401 4560 : sp->sibling = NULL;
402 4560 : gpr_ref_init(&sp->refs, 1);
403 4560 : GPR_ASSERT(sp->emfd);
404 4560 : gpr_mu_unlock(&s->mu);
405 4560 : gpr_free(addr_str);
406 4560 : gpr_free(name);
407 : }
408 :
409 4560 : return sp;
410 : }
411 :
412 4545 : grpc_tcp_listener *grpc_tcp_server_add_port(grpc_tcp_server *s,
413 : const void *addr, size_t addr_len) {
414 : grpc_tcp_listener *sp;
415 4498 : grpc_tcp_listener *sp2 = NULL;
416 : int fd;
417 : grpc_dualstack_mode dsmode;
418 : struct sockaddr_in6 addr6_v4mapped;
419 : struct sockaddr_in wild4;
420 : struct sockaddr_in6 wild6;
421 : struct sockaddr_in addr4_copy;
422 4498 : struct sockaddr *allocated_addr = NULL;
423 : struct sockaddr_storage sockname_temp;
424 : socklen_t sockname_len;
425 : int port;
426 :
427 4545 : if (((struct sockaddr *)addr)->sa_family == AF_UNIX) {
428 496 : unlink_if_unix_domain_socket(addr);
429 : }
430 :
431 : /* Check if this is a wildcard port, and if so, try to keep the port the same
432 : as some previously created listener. */
433 4545 : if (grpc_sockaddr_get_port(addr) == 0) {
434 135 : for (sp = s->head; sp; sp = sp->next) {
435 42 : sockname_len = sizeof(sockname_temp);
436 42 : if (0 == getsockname(sp->fd, (struct sockaddr *)&sockname_temp,
437 : &sockname_len)) {
438 42 : port = grpc_sockaddr_get_port((struct sockaddr *)&sockname_temp);
439 42 : if (port > 0) {
440 42 : allocated_addr = malloc(addr_len);
441 42 : memcpy(allocated_addr, addr, addr_len);
442 42 : grpc_sockaddr_set_port(allocated_addr, port);
443 25 : addr = allocated_addr;
444 42 : break;
445 : }
446 : }
447 : }
448 : }
449 :
450 4498 : sp = NULL;
451 :
452 4545 : if (grpc_sockaddr_to_v4mapped(addr, &addr6_v4mapped)) {
453 2197 : addr = (const struct sockaddr *)&addr6_v4mapped;
454 2197 : addr_len = sizeof(addr6_v4mapped);
455 : }
456 :
457 : /* Treat :: or 0.0.0.0 as a family-agnostic wildcard. */
458 4545 : if (grpc_sockaddr_is_wildcard(addr, &port)) {
459 84 : grpc_sockaddr_make_wildcards(port, &wild4, &wild6);
460 :
461 : /* Try listening on IPv6 first. */
462 71 : addr = (struct sockaddr *)&wild6;
463 71 : addr_len = sizeof(wild6);
464 84 : fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
465 84 : sp = add_socket_to_server(s, fd, addr, addr_len);
466 84 : if (fd >= 0 && dsmode == GRPC_DSMODE_DUALSTACK) {
467 56 : goto done;
468 : }
469 :
470 : /* If we didn't get a dualstack socket, also listen on 0.0.0.0. */
471 15 : if (port == 0 && sp != NULL) {
472 0 : grpc_sockaddr_set_port((struct sockaddr *)&wild4, sp->port);
473 0 : sp2 = sp;
474 : }
475 15 : addr = (struct sockaddr *)&wild4;
476 15 : addr_len = sizeof(wild4);
477 : }
478 :
479 4476 : fd = grpc_create_dualstack_socket(addr, SOCK_STREAM, 0, &dsmode);
480 4476 : if (fd < 0) {
481 0 : gpr_log(GPR_ERROR, "Unable to create socket: %s", strerror(errno));
482 : }
483 4499 : if (dsmode == GRPC_DSMODE_IPV4 &&
484 23 : grpc_sockaddr_is_v4mapped(addr, &addr4_copy)) {
485 8 : addr = (struct sockaddr *)&addr4_copy;
486 8 : addr_len = sizeof(addr4_copy);
487 : }
488 4476 : sp = add_socket_to_server(s, fd, addr, addr_len);
489 4476 : if (sp != NULL) sp->sibling = sp2;
490 4476 : if (sp2 != NULL) sp2->is_sibling = 1;
491 :
492 : done:
493 4545 : gpr_free(allocated_addr);
494 4545 : return sp;
495 : }
496 :
497 2 : int grpc_tcp_server_get_fd(grpc_tcp_server *s, unsigned port_index) {
498 : grpc_tcp_listener *sp;
499 2 : for (sp = s->head; sp && port_index != 0; sp = sp->next, port_index--)
500 : ;
501 2 : if (port_index == 0 && sp) {
502 2 : return sp->fd;
503 : } else {
504 0 : return -1;
505 : }
506 : }
507 :
508 2759 : void grpc_tcp_server_start(grpc_exec_ctx *exec_ctx, grpc_tcp_server *s,
509 : grpc_pollset **pollsets, size_t pollset_count,
510 : grpc_tcp_server_cb on_accept_cb,
511 : void *on_accept_cb_arg) {
512 : size_t i;
513 : grpc_tcp_listener *sp;
514 2759 : GPR_ASSERT(on_accept_cb);
515 2759 : gpr_mu_lock(&s->mu);
516 2759 : GPR_ASSERT(!s->on_accept_cb);
517 2759 : GPR_ASSERT(s->active_ports == 0);
518 2759 : s->on_accept_cb = on_accept_cb;
519 2759 : s->on_accept_cb_arg = on_accept_cb_arg;
520 2759 : s->pollsets = pollsets;
521 2759 : s->pollset_count = pollset_count;
522 7315 : for (sp = s->head; sp; sp = sp->next) {
523 9190 : for (i = 0; i < pollset_count; i++) {
524 4679 : grpc_pollset_add_fd(exec_ctx, pollsets[i], sp->emfd);
525 : }
526 4556 : sp->read_closure.cb = on_read;
527 4556 : sp->read_closure.cb_arg = sp;
528 4556 : grpc_fd_notify_on_read(exec_ctx, sp->emfd, &sp->read_closure);
529 4556 : s->active_ports++;
530 : }
531 2759 : gpr_mu_unlock(&s->mu);
532 2759 : }
533 :
534 4541 : int grpc_tcp_listener_get_port(grpc_tcp_listener *listener) {
535 4494 : grpc_tcp_listener *sp = listener;
536 4541 : return sp->port;
537 : }
538 :
539 0 : void grpc_tcp_listener_ref(grpc_tcp_listener *listener) {
540 0 : grpc_tcp_listener *sp = listener;
541 0 : gpr_ref(&sp->refs);
542 0 : }
543 :
544 4558 : void grpc_tcp_listener_unref(grpc_tcp_listener *listener) {
545 4513 : grpc_tcp_listener *sp = listener;
546 9116 : if (sp->is_sibling) return;
547 4558 : if (gpr_unref(&sp->refs)) {
548 4558 : grpc_tcp_listener *sibling = sp->sibling;
549 9116 : while (sibling) {
550 0 : sp = sibling;
551 0 : sibling = sp->sibling;
552 0 : gpr_free(sp);
553 : }
554 4558 : gpr_free(listener);
555 : }
556 : }
557 :
558 : #endif
|