1/*** 2 This file is part of PulseAudio. 3 4 Copyright 2004-2006 Lennart Poettering 5 Copyright 2006-2007 Pierre Ossman <ossman@cendio.se> for Cendio AB 6 7 PulseAudio is free software; you can redistribute it and/or modify 8 it under the terms of the GNU Lesser General Public License as published 9 by the Free Software Foundation; either version 2.1 of the License, 10 or (at your option) any later version. 11 12 PulseAudio is distributed in the hope that it will be useful, but 13 WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 General Public License for more details. 16 17 You should have received a copy of the GNU Lesser General Public License 18 along with PulseAudio; if not, see <http://www.gnu.org/licenses/>. 19***/ 20 21#ifdef HAVE_CONFIG_H 22#include <config.h> 23#endif 24 25#include <stdlib.h> 26#include <errno.h> 27#include <string.h> 28#include <sys/types.h> 29#include <stdio.h> 30#include <unistd.h> 31#include <sys/stat.h> 32 33#ifdef HAVE_SYS_UN_H 34#include <sys/un.h> 35#ifndef SUN_LEN 36#define SUN_LEN(ptr) \ 37 ((size_t)(((struct sockaddr_un *) 0)->sun_path) + strlen((ptr)->sun_path)) 38#endif 39#endif 40#ifdef HAVE_NETINET_IN_H 41#include <netinet/in.h> 42#endif 43 44#ifdef HAVE_LIBWRAP 45#include <tcpd.h> 46 47/* Solaris requires that the allow_severity and deny_severity variables be 48 * defined in the client program. */ 49#ifdef __sun 50#include <syslog.h> 51int allow_severity = LOG_INFO; 52int deny_severity = LOG_WARNING; 53#endif 54 55#endif /* HAVE_LIBWRAP */ 56 57#ifdef HAVE_SYSTEMD_DAEMON 58#include <systemd/sd-daemon.h> 59#endif 60 61#ifdef HAVE_WINDOWS_H 62#include <windows.h> 63#include <aclapi.h> 64#include <sddl.h> 65#endif 66 67#include <pulse/xmalloc.h> 68#include <pulse/util.h> 69 70#include <pulsecore/socket.h> 71#include <pulsecore/socket-util.h> 72#include <pulsecore/core-util.h> 73#include <pulsecore/log.h> 74#include <pulsecore/macro.h> 75#include <pulsecore/core-error.h> 76#include <pulsecore/refcnt.h> 77#include <pulsecore/arpa-inet.h> 78 79#include "socket-server.h" 80 81struct pa_socket_server { 82 PA_REFCNT_DECLARE; 83 int fd; 84 char *filename; 85 bool activated; 86 char *tcpwrap_service; 87 88 pa_socket_server_on_connection_cb_t on_connection; 89 void *userdata; 90 91 pa_io_event *io_event; 92 pa_mainloop_api *mainloop; 93 enum { 94 SOCKET_SERVER_IPV4, 95 SOCKET_SERVER_UNIX, 96 SOCKET_SERVER_IPV6 97 } type; 98}; 99 100static void callback(pa_mainloop_api *mainloop, pa_io_event *e, int fd, pa_io_event_flags_t f, void *userdata) { 101 pa_socket_server *s = userdata; 102 pa_iochannel *io; 103 int nfd; 104 105 pa_assert(s); 106 pa_assert(PA_REFCNT_VALUE(s) >= 1); 107 pa_assert(s->mainloop == mainloop); 108 pa_assert(s->io_event == e); 109 pa_assert(e); 110 pa_assert(fd >= 0); 111 pa_assert(fd == s->fd); 112 113 pa_socket_server_ref(s); 114 115 if ((nfd = pa_accept_cloexec(fd, NULL, NULL)) < 0) { 116 pa_log("accept(): %s", pa_cstrerror(errno)); 117 goto finish; 118 } 119 120 if (!s->on_connection) { 121 pa_close(nfd); 122 goto finish; 123 } 124 125#ifdef HAVE_LIBWRAP 126 127 if (s->tcpwrap_service) { 128 struct request_info req; 129 130 request_init(&req, RQ_DAEMON, s->tcpwrap_service, RQ_FILE, nfd, NULL); 131 fromhost(&req); 132 if (!hosts_access(&req)) { 133 pa_log_warn("TCP connection refused by tcpwrap."); 134 pa_close(nfd); 135 goto finish; 136 } 137 138 pa_log_info("TCP connection accepted by tcpwrap."); 139 } 140#endif 141 142 /* There should be a check for socket type here */ 143 if (s->type == SOCKET_SERVER_IPV4) 144 pa_make_tcp_socket_low_delay(nfd); 145 else 146 pa_make_socket_low_delay(nfd); 147 148 pa_assert_se(io = pa_iochannel_new(s->mainloop, nfd, nfd)); 149 s->on_connection(s, io, s->userdata); 150 151finish: 152 pa_socket_server_unref(s); 153} 154 155static pa_socket_server* socket_server_new(pa_mainloop_api *m, int fd) { 156 pa_socket_server *s; 157 158 pa_assert(m); 159 pa_assert(fd >= 0); 160 161 s = pa_xnew0(pa_socket_server, 1); 162 PA_REFCNT_INIT(s); 163 s->fd = fd; 164 s->mainloop = m; 165 166 pa_assert_se(s->io_event = m->io_new(m, fd, PA_IO_EVENT_INPUT, callback, s)); 167 168 return s; 169} 170 171pa_socket_server* pa_socket_server_ref(pa_socket_server *s) { 172 pa_assert(s); 173 pa_assert(PA_REFCNT_VALUE(s) >= 1); 174 175 PA_REFCNT_INC(s); 176 return s; 177} 178 179#ifdef HAVE_SYS_UN_H 180 181pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { 182 int fd = -1; 183 bool activated = false; 184 struct sockaddr_un sa; 185 pa_socket_server *s; 186 187 pa_assert(m); 188 pa_assert(filename); 189 190#ifdef HAVE_SYSTEMD_DAEMON 191 { 192 int n = sd_listen_fds(0); 193 if (n > 0) { 194 for (int i = 0; i < n; ++i) { 195 if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, filename, 0) > 0) { 196 fd = SD_LISTEN_FDS_START + i; 197 activated = true; 198 pa_log_info("Found socket activation socket for '%s' \\o/", filename); 199 break; 200 } 201 } 202 } 203 } 204#endif 205 206 if (fd < 0) { 207 if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) { 208 pa_log("socket(PF_UNIX): %s", pa_cstrerror(errno)); 209 goto fail; 210 } 211 212 memset(&sa, 0, sizeof(sa)); 213 sa.sun_family = AF_UNIX; 214 pa_strlcpy(sa.sun_path, filename, sizeof(sa.sun_path)); 215 216 pa_make_socket_low_delay(fd); 217 218 if (bind(fd, (struct sockaddr*) &sa, (socklen_t) SUN_LEN(&sa)) < 0) { 219 pa_log("bind(): %s", pa_cstrerror(errno)); 220 goto fail; 221 } 222 223 /* Allow access from all clients. Sockets like this one should 224 * always be put inside a directory with proper access rights, 225 * because not all OS check the access rights on the socket 226 * inodes. */ 227 chmod(filename, 0777); 228 229#ifdef OS_IS_WIN32 230 /* https://docs.microsoft.com/en-us/windows/win32/secauthz/ace-strings */ 231 /* https://docs.microsoft.com/en-us/windows/win32/secauthz/modifying-the-acls-of-an-object-in-c-- */ 232 /* https://docs.microsoft.com/en-us/windows/win32/api/sddl/nf-sddl-convertstringsecuritydescriptortosecuritydescriptora */ 233 PSECURITY_DESCRIPTOR sd; 234 if (ConvertStringSecurityDescriptorToSecurityDescriptorA( 235 "D:" /* DACL */ 236 "(A;;FRFW;;;WD)", /* allow all users to read/write */ 237 SDDL_REVISION_1, &sd, NULL 238 )) { 239 PACL acl; 240 BOOL acl_present, acl_default; 241 if (GetSecurityDescriptorDacl(sd, &acl_present, &acl, &acl_default)) { 242 if (SetNamedSecurityInfo(filename, SE_FILE_OBJECT, DACL_SECURITY_INFORMATION, NULL, NULL, acl, NULL) != ERROR_SUCCESS) { 243 pa_log_warn("Failed to set DACL for socket: failed to apply DACL: error %lu.", GetLastError()); 244 } 245 LocalFree(acl); 246 } else { 247 pa_log_warn("Failed to set DACL for socket: failed to get security descriptor DACL: error %lu.", GetLastError()); 248 } 249 } else { 250 pa_log_warn("Failed to set DACL for socket: failed to parse security descriptor: error %lu.", GetLastError()); 251 } 252#endif 253 254 if (listen(fd, 5) < 0) { 255 pa_log("listen(): %s", pa_cstrerror(errno)); 256 goto fail; 257 } 258 } 259 260 pa_assert_se(s = socket_server_new(m, fd)); 261 262 s->filename = pa_xstrdup(filename); 263 s->type = SOCKET_SERVER_UNIX; 264 s->activated = activated; 265 266 return s; 267 268fail: 269 if (fd >= 0) 270 pa_close(fd); 271 272 return NULL; 273} 274 275#else /* HAVE_SYS_UN_H */ 276 277pa_socket_server* pa_socket_server_new_unix(pa_mainloop_api *m, const char *filename) { 278 return NULL; 279} 280 281#endif /* HAVE_SYS_UN_H */ 282 283pa_socket_server* pa_socket_server_new_ipv4(pa_mainloop_api *m, uint32_t address, uint16_t port, bool fallback, const char *tcpwrap_service) { 284 pa_socket_server *ss; 285 int fd = -1; 286 bool activated = false; 287 struct sockaddr_in sa; 288 int on = 1; 289 290 pa_assert(m); 291 pa_assert(port); 292 293#ifdef HAVE_SYSTEMD_DAEMON 294 { 295 int n = sd_listen_fds(0); 296 if (n > 0) { 297 for (int i = 0; i < n; ++i) { 298 if (sd_is_socket_inet(SD_LISTEN_FDS_START + i, AF_INET, SOCK_STREAM, 1, port) > 0) { 299 fd = SD_LISTEN_FDS_START + i; 300 activated = true; 301 pa_log_info("Found socket activation socket for ipv4 in port '%d' \\o/", port); 302 break; 303 } 304 } 305 } 306 } 307#endif 308 309 if (fd < 0) { 310 if ((fd = pa_socket_cloexec(PF_INET, SOCK_STREAM, 0)) < 0) { 311 pa_log("socket(PF_INET): %s", pa_cstrerror(errno)); 312 goto fail; 313 } 314 315#ifdef SO_REUSEADDR 316 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0) 317 pa_log("setsockopt(): %s", pa_cstrerror(errno)); 318#endif 319 320 pa_make_tcp_socket_low_delay(fd); 321 322 memset(&sa, 0, sizeof(sa)); 323 sa.sin_family = AF_INET; 324 sa.sin_port = htons(port); 325 sa.sin_addr.s_addr = htonl(address); 326 327 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 328 329 if (errno == EADDRINUSE && fallback) { 330 sa.sin_port = 0; 331 332 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 333 pa_log("bind(): %s", pa_cstrerror(errno)); 334 goto fail; 335 } 336 } else { 337 pa_log("bind(): %s", pa_cstrerror(errno)); 338 goto fail; 339 } 340 } 341 342 if (listen(fd, 5) < 0) { 343 pa_log("listen(): %s", pa_cstrerror(errno)); 344 goto fail; 345 } 346 } 347 348 pa_assert_se(ss = socket_server_new(m, fd)); 349 350 ss->type = SOCKET_SERVER_IPV4; 351 ss->tcpwrap_service = pa_xstrdup(tcpwrap_service); 352 ss->activated = activated; 353 354 return ss; 355 356fail: 357 if (fd >= 0) 358 pa_close(fd); 359 360 return NULL; 361} 362 363#ifdef HAVE_IPV6 364pa_socket_server* pa_socket_server_new_ipv6(pa_mainloop_api *m, const uint8_t address[16], uint16_t port, bool fallback, const char *tcpwrap_service) { 365 pa_socket_server *ss; 366 int fd = -1; 367 bool activated = false; 368 struct sockaddr_in6 sa; 369 int on; 370 371 pa_assert(m); 372 pa_assert(port > 0); 373 374#ifdef HAVE_SYSTEMD_DAEMON 375 { 376 int n = sd_listen_fds(0); 377 if (n > 0) { 378 for (int i = 0; i < n; ++i) { 379 if (sd_is_socket_inet(SD_LISTEN_FDS_START + i, AF_INET6, SOCK_STREAM, 1, port) > 0) { 380 fd = SD_LISTEN_FDS_START + i; 381 activated = true; 382 pa_log_info("Found socket activation socket for ipv6 in port '%d' \\o/", port); 383 break; 384 } 385 } 386 } 387 } 388#endif 389 390 if (fd < 0) { 391 if ((fd = pa_socket_cloexec(PF_INET6, SOCK_STREAM, 0)) < 0) { 392 pa_log("socket(PF_INET6): %s", pa_cstrerror(errno)); 393 goto fail; 394 } 395 396#ifdef IPV6_V6ONLY 397 on = 1; 398 if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, (const void *) &on, sizeof(on)) < 0) 399 pa_log("setsockopt(IPPROTO_IPV6, IPV6_V6ONLY): %s", pa_cstrerror(errno)); 400#endif 401 402#ifdef SO_REUSEADDR 403 on = 1; 404 if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (const void *) &on, sizeof(on)) < 0) 405 pa_log("setsockopt(SOL_SOCKET, SO_REUSEADDR, 1): %s", pa_cstrerror(errno)); 406#endif 407 408 pa_make_tcp_socket_low_delay(fd); 409 410 memset(&sa, 0, sizeof(sa)); 411 sa.sin6_family = AF_INET6; 412 sa.sin6_port = htons(port); 413 memcpy(sa.sin6_addr.s6_addr, address, 16); 414 415 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 416 417 if (errno == EADDRINUSE && fallback) { 418 sa.sin6_port = 0; 419 420 if (bind(fd, (struct sockaddr *) &sa, sizeof(sa)) < 0) { 421 pa_log("bind(): %s", pa_cstrerror(errno)); 422 goto fail; 423 } 424 } else { 425 pa_log("bind(): %s", pa_cstrerror(errno)); 426 goto fail; 427 } 428 } 429 430 if (listen(fd, 5) < 0) { 431 pa_log("listen(): %s", pa_cstrerror(errno)); 432 goto fail; 433 } 434 } 435 436 pa_assert_se(ss = socket_server_new(m, fd)); 437 438 ss->type = SOCKET_SERVER_IPV6; 439 ss->tcpwrap_service = pa_xstrdup(tcpwrap_service); 440 ss->activated = activated; 441 442 return ss; 443 444fail: 445 if (fd >= 0) 446 pa_close(fd); 447 448 return NULL; 449} 450#endif 451 452pa_socket_server* pa_socket_server_new_ipv4_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) { 453 pa_assert(m); 454 pa_assert(port > 0); 455 456 return pa_socket_server_new_ipv4(m, INADDR_LOOPBACK, port, fallback, tcpwrap_service); 457} 458 459#ifdef HAVE_IPV6 460pa_socket_server* pa_socket_server_new_ipv6_loopback(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) { 461 pa_assert(m); 462 pa_assert(port > 0); 463 464 return pa_socket_server_new_ipv6(m, in6addr_loopback.s6_addr, port, fallback, tcpwrap_service); 465} 466#endif 467 468pa_socket_server* pa_socket_server_new_ipv4_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) { 469 pa_assert(m); 470 pa_assert(port > 0); 471 472 return pa_socket_server_new_ipv4(m, INADDR_ANY, port, fallback, tcpwrap_service); 473} 474 475#ifdef HAVE_IPV6 476pa_socket_server* pa_socket_server_new_ipv6_any(pa_mainloop_api *m, uint16_t port, bool fallback, const char *tcpwrap_service) { 477 pa_assert(m); 478 pa_assert(port > 0); 479 480 return pa_socket_server_new_ipv6(m, in6addr_any.s6_addr, port, fallback, tcpwrap_service); 481} 482#endif 483 484pa_socket_server* pa_socket_server_new_ipv4_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service) { 485 struct in_addr ipv4; 486 487 pa_assert(m); 488 pa_assert(name); 489 pa_assert(port > 0); 490 491 if (inet_pton(AF_INET, name, &ipv4) > 0) 492 return pa_socket_server_new_ipv4(m, ntohl(ipv4.s_addr), port, fallback, tcpwrap_service); 493 494 return NULL; 495} 496 497#ifdef HAVE_IPV6 498pa_socket_server* pa_socket_server_new_ipv6_string(pa_mainloop_api *m, const char *name, uint16_t port, bool fallback, const char *tcpwrap_service) { 499 struct in6_addr ipv6; 500 501 pa_assert(m); 502 pa_assert(name); 503 pa_assert(port > 0); 504 505 if (inet_pton(AF_INET6, name, &ipv6) > 0) 506 return pa_socket_server_new_ipv6(m, ipv6.s6_addr, port, fallback, tcpwrap_service); 507 508 return NULL; 509} 510#endif 511 512static void socket_server_free(pa_socket_server*s) { 513 pa_assert(s); 514 515 if (!s->activated && s->filename) 516 unlink(s->filename); 517 pa_xfree(s->filename); 518 519 pa_close(s->fd); 520 521 pa_xfree(s->tcpwrap_service); 522 523 s->mainloop->io_free(s->io_event); 524 pa_xfree(s); 525} 526 527void pa_socket_server_unref(pa_socket_server *s) { 528 pa_assert(s); 529 pa_assert(PA_REFCNT_VALUE(s) >= 1); 530 531 if (PA_REFCNT_DEC(s) <= 0) 532 socket_server_free(s); 533} 534 535void pa_socket_server_set_callback(pa_socket_server*s, pa_socket_server_on_connection_cb_t on_connection, void *userdata) { 536 pa_assert(s); 537 pa_assert(PA_REFCNT_VALUE(s) >= 1); 538 539 s->on_connection = on_connection; 540 s->userdata = userdata; 541} 542 543char *pa_socket_server_get_address(pa_socket_server *s, char *c, size_t l) { 544 pa_assert(s); 545 pa_assert(PA_REFCNT_VALUE(s) >= 1); 546 pa_assert(c); 547 pa_assert(l > 0); 548 549 switch (s->type) { 550#ifdef HAVE_IPV6 551 case SOCKET_SERVER_IPV6: { 552 struct sockaddr_in6 sa; 553 socklen_t sa_len = sizeof(sa); 554 555 if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { 556 pa_log("getsockname(): %s", pa_cstrerror(errno)); 557 return NULL; 558 } 559 560 if (memcmp(&in6addr_any, &sa.sin6_addr, sizeof(in6addr_any)) == 0) { 561 char fqdn[256]; 562 if (!pa_get_fqdn(fqdn, sizeof(fqdn))) 563 return NULL; 564 565 pa_snprintf(c, l, "tcp6:%s:%u", fqdn, (unsigned) ntohs(sa.sin6_port)); 566 567 } else if (memcmp(&in6addr_loopback, &sa.sin6_addr, sizeof(in6addr_loopback)) == 0) { 568 char *id; 569 570 if (!(id = pa_machine_id())) 571 return NULL; 572 573 pa_snprintf(c, l, "{%s}tcp6:localhost:%u", id, (unsigned) ntohs(sa.sin6_port)); 574 pa_xfree(id); 575 } else { 576 char ip[INET6_ADDRSTRLEN]; 577 578 if (!inet_ntop(AF_INET6, &sa.sin6_addr, ip, sizeof(ip))) { 579 pa_log("inet_ntop(): %s", pa_cstrerror(errno)); 580 return NULL; 581 } 582 583 pa_snprintf(c, l, "tcp6:[%s]:%u", ip, (unsigned) ntohs(sa.sin6_port)); 584 } 585 586 return c; 587 } 588#endif 589 590 case SOCKET_SERVER_IPV4: { 591 struct sockaddr_in sa; 592 socklen_t sa_len = sizeof(sa); 593 594 if (getsockname(s->fd, (struct sockaddr*) &sa, &sa_len) < 0) { 595 pa_log("getsockname(): %s", pa_cstrerror(errno)); 596 return NULL; 597 } 598 599 if (sa.sin_addr.s_addr == INADDR_ANY) { 600 char fqdn[256]; 601 if (!pa_get_fqdn(fqdn, sizeof(fqdn))) 602 return NULL; 603 604 pa_snprintf(c, l, "tcp:%s:%u", fqdn, (unsigned) ntohs(sa.sin_port)); 605 } else if (sa.sin_addr.s_addr == INADDR_LOOPBACK) { 606 char *id; 607 608 if (!(id = pa_machine_id())) 609 return NULL; 610 611 pa_snprintf(c, l, "{%s}tcp:localhost:%u", id, (unsigned) ntohs(sa.sin_port)); 612 pa_xfree(id); 613 } else { 614 char ip[INET_ADDRSTRLEN]; 615 616 if (!inet_ntop(AF_INET, &sa.sin_addr, ip, sizeof(ip))) { 617 pa_log("inet_ntop(): %s", pa_cstrerror(errno)); 618 return NULL; 619 } 620 621 pa_snprintf(c, l, "tcp:[%s]:%u", ip, (unsigned) ntohs(sa.sin_port)); 622 } 623 624 return c; 625 } 626 627 case SOCKET_SERVER_UNIX: { 628 char *id; 629 630 if (!s->filename) 631 return NULL; 632 633 if (!(id = pa_machine_id())) 634 return NULL; 635 636 pa_snprintf(c, l, "{%s}unix:%s", id, s->filename); 637 pa_xfree(id); 638 return c; 639 } 640 641 default: 642 return NULL; 643 } 644} 645 646#ifdef HAVE_SYS_UN_H 647 648int pa_unix_socket_is_stale(const char *fn) { 649 struct sockaddr_un sa; 650 int fd = -1, ret = -1; 651 652 pa_assert(fn); 653 654 if ((fd = pa_socket_cloexec(PF_UNIX, SOCK_STREAM, 0)) < 0) { 655 pa_log("socket(): %s", pa_cstrerror(errno)); 656 goto finish; 657 } 658 659 sa.sun_family = AF_UNIX; 660 strncpy(sa.sun_path, fn, sizeof(sa.sun_path)-1); 661 sa.sun_path[sizeof(sa.sun_path) - 1] = 0; 662 663 if (connect(fd, (struct sockaddr*) &sa, sizeof(sa)) < 0) { 664#if !defined(OS_IS_WIN32) 665 if (errno == ECONNREFUSED) 666 ret = 1; 667#else 668 if (WSAGetLastError() == WSAECONNREFUSED || WSAGetLastError() == WSAEINVAL) 669 ret = 1; 670#endif 671 } else 672 ret = 0; 673 674finish: 675 if (fd >= 0) 676 pa_close(fd); 677 678 return ret; 679} 680 681int pa_unix_socket_remove_stale(const char *fn) { 682 int r; 683 684 pa_assert(fn); 685 686#ifdef HAVE_SYSTEMD_DAEMON 687 { 688 int n = sd_listen_fds(0); 689 if (n > 0) { 690 for (int i = 0; i < n; ++i) { 691 if (sd_is_socket_unix(SD_LISTEN_FDS_START + i, SOCK_STREAM, 1, fn, 0) > 0) { 692 /* This is a socket activated socket, therefore do not consider 693 * it stale. */ 694 return 0; 695 } 696 } 697 } 698 } 699#endif 700 701 if ((r = pa_unix_socket_is_stale(fn)) < 0) 702 return errno != ENOENT ? -1 : 0; 703 704 if (!r) 705 return 0; 706 707 /* Yes, here is a race condition. But who cares? */ 708 if (unlink(fn) < 0) 709 return -1; 710 711 return 0; 712} 713 714#else /* HAVE_SYS_UN_H */ 715 716int pa_unix_socket_is_stale(const char *fn) { 717 return -1; 718} 719 720int pa_unix_socket_remove_stale(const char *fn) { 721 return -1; 722} 723 724#endif /* HAVE_SYS_UN_H */ 725