vinyl-cache/lib/libvinyl/vus.c
0
/*-
1
 * Copyright 2018 UPLEX - Nils Goroll Systemoptimierung
2
 * All rights reserved.
3
 *
4
 * Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
5
 *
6
 * SPDX-License-Identifier: BSD-2-Clause
7
 *
8
 * Redistribution and use in source and binary forms, with or without
9
 * modification, are permitted provided that the following conditions
10
 * are met:
11
 * 1. Redistributions of source code must retain the above copyright
12
 *    notice, this list of conditions and the following disclaimer.
13
 * 2. Redistributions in binary form must reproduce the above copyright
14
 *    notice, this list of conditions and the following disclaimer in the
15
 *    documentation and/or other materials provided with the distribution.
16
 *
17
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
18
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20
 * ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
21
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
22
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
23
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
24
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
26
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
27
 * SUCH DAMAGE.
28
 */
29
30
#include <sys/socket.h>
31
#include <sys/un.h>
32
33
#include <unistd.h>
34
#include <string.h>
35
#include <poll.h>
36
#include <stdio.h>
37
38
#include "vdef.h"
39
#include "vas.h"
40
#include "vus.h"
41
#include "vtcp.h"
42
43
static int
44 2042
sun_init(struct sockaddr_un *uds, const char *path, const char **err)
45
{
46 2042
        AN(uds);
47 2042
        AN(path);
48 2042
        assert(VUS_is(path));
49
50 2042
        if (err)
51 1683
                *err = NULL;
52
53 2042
        if (vstrlen(path) + 1 > sizeof(uds->sun_path)) {
54 6
                errno = ENAMETOOLONG;
55 6
                if (err)
56 6
                        *err = "Path too long for a Unix domain socket";
57 6
                return (-1);
58
        }
59 2036
        if (! vstrcmp(path, "@")) {
60 0
                errno = EINVAL;
61 0
                if (err)
62 0
                        *err = "The empty abstract socket name is not"
63
                            " supported";
64 0
                return (-1);
65
        }
66 2036
        memset(uds->sun_path, 0, sizeof(uds->sun_path));
67 2036
        if (*path == '@')
68 3
                bprintf(uds->sun_path, "%c%s", 0, path + 1);
69
        else
70 2033
                bprintf(uds->sun_path, "%s", path);
71 2036
        uds->sun_family = PF_UNIX;
72 2036
        return (0);
73 2042
}
74
75
int
76 1686
VUS_resolver(const char *path, vus_resolved_f *func, void *priv,
77
             const char **err)
78
{
79
        struct sockaddr_un uds;
80
        int ret;
81
82 1686
        AN(err);
83
84 1686
        ret = sun_init(&uds, path, err);
85 1686
        if (ret)
86 6
                return (ret);
87
88 1680
        assert(uds.sun_path[1] != '\0');
89
90 1680
        if (func != NULL)
91 1677
                ret = func(priv, &uds);
92 1680
        return (ret);
93 1680
}
94
95
int
96 399
VUS_bind(const struct sockaddr_un *uds, const char **errp)
97
{
98
        int sd, e;
99
        socklen_t sl;
100
101 399
        sl = VUS_socklen(uds);
102
103 399
        if (errp != NULL)
104 180
                *errp = NULL;
105
106 399
        sd = socket(PF_UNIX, SOCK_STREAM, 0);
107 399
        if (sd < 0) {
108 0
                if (errp != NULL)
109 0
                        *errp = "socket(2)";
110 0
                return (-1);
111
        }
112
113 399
        if (unlink(uds->sun_path) != 0 && errno != ENOENT) {
114 0
                if (errp != NULL)
115 0
                        *errp = "unlink(2)";
116 0
                e = errno;
117 0
                closefd(&sd);
118 0
                errno = e;
119 0
                return (-1);
120
        }
121
122 399
        if (bind(sd, (const void*)uds, sl) != 0) {
123 3
                if (errp != NULL)
124 0
                        *errp = "bind(2)";
125 3
                e = errno;
126 3
                closefd(&sd);
127 3
                errno = e;
128 3
                return (-1);
129
        }
130 396
        return (sd);
131 399
}
132
133
int
134 359
VUS_connect(const char *path, int msec)
135
{
136
        int s, i;
137
        struct pollfd fds[1];
138
        struct sockaddr_un uds;
139
        socklen_t sl;
140
141 359
        if (path == NULL)
142 0
                return (-1);
143 359
        i = sun_init(&uds, path, NULL);
144 359
        if (i)
145 0
                return (i);
146
147 359
        assert(uds.sun_path[1] != '\0');
148
149 359
        sl = VUS_socklen(&uds);
150
151 359
        AN(sl);
152
153 359
        s = socket(PF_UNIX, SOCK_STREAM, 0);
154 359
        if (s < 0)
155 0
                return (s);
156
157
        /* Set the socket non-blocking */
158 359
        if (msec != 0)
159 359
                VTCP_nonblocking(s);
160
161 359
        i = connect(s, (const void*)&uds, sl);
162 359
        if (i == 0 && msec > 0)
163 355
                VTCP_blocking(s);
164 359
        if (i == 0)
165 355
                return (s);
166 4
        if (errno != EINPROGRESS) {
167 4
                closefd(&s);
168 4
                return (-1);
169
        }
170
171 0
        if (msec < 0) {
172
                /*
173
                 * Caller is responsible for waiting and
174
                 * calling VTCP_connected
175
                 */
176 0
                return (s);
177
        }
178
179 0
        assert(msec > 0);
180
        /* Exercise our patience, polling for write */
181 0
        fds[0].fd = s;
182 0
        fds[0].events = POLLWRNORM;
183 0
        fds[0].revents = 0;
184 0
        i = poll(fds, 1, msec);
185
186 0
        if (i == 0) {
187
                /* Timeout, close and give up */
188 0
                closefd(&s);
189 0
                errno = ETIMEDOUT;
190 0
                return (-1);
191
        }
192
193 0
        return (VTCP_connected(s));
194 359
}
195
196
socklen_t
197 1814
VUS_socklen(const struct sockaddr_un *uds)
198
{
199
        socklen_t sl;
200
        const char *p;
201 1814
        if (*uds->sun_path)
202 1811
                sl = sizeof(*uds);
203
        else {
204 3
                p = strchr(uds->sun_path + 1, '\0');
205 3
                assert(p != NULL);
206 3
                sl = p - (const char*)uds;
207
        }
208 1814
        assert(sl <= sizeof(*uds));
209 1814
        return sl;
210
}