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