e05e8e4fc28864bd28f5dcbe0b3d9c1423e96375
[src/drm-lease-manager.git] / drm-lease-manager / lease-server.c
1 /* Copyright 2020-2021 IGEL Co., Ltd.
2  *
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15
16 #include "lease-server.h"
17 #include "log.h"
18 #include "socket-path.h"
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <poll.h>
24 #include <stdlib.h>
25 #include <sys/epoll.h>
26 #include <sys/file.h>
27 #include <sys/socket.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <sys/un.h>
31 #include <unistd.h>
32
33 #define SOCK_LOCK_SUFFIX ".lock"
34
35 struct ls_socket {
36         int fd;
37         struct ls_server *serv;
38 };
39
40 struct ls_server {
41         struct lease_handle *lease_handle;
42         struct sockaddr_un address;
43         int server_socket_lock;
44
45         struct ls_socket listen;
46         struct ls_socket client;
47
48         bool is_client_connected;
49 };
50
51 struct ls {
52         int epoll_fd;
53
54         struct ls_server *servers;
55         int nservers;
56 };
57
58 static bool client_connect(struct ls *ls, struct ls_server *serv)
59 {
60         int cfd = accept(serv->listen.fd, NULL, NULL);
61         if (cfd < 0) {
62                 DEBUG_LOG("accept failed on %s: %s\n", serv->address.sun_path,
63                           strerror(errno));
64                 return false;
65         }
66
67         if (serv->is_client_connected) {
68                 WARN_LOG("Client already connected on %s\n",
69                          serv->address.sun_path);
70                 close(cfd);
71                 return false;
72         }
73
74         serv->client.fd = cfd;
75         serv->client.serv = serv;
76
77         struct epoll_event ev = {
78             .events = POLLHUP,
79             .data.ptr = &serv->client,
80         };
81         if (epoll_ctl(ls->epoll_fd, EPOLL_CTL_ADD, cfd, &ev)) {
82                 DEBUG_LOG("epoll_ctl add failed: %s\n", strerror(errno));
83                 close(cfd);
84                 return false;
85         }
86
87         serv->is_client_connected = true;
88         return true;
89 }
90
91 static int create_socket_lock(struct sockaddr_un *addr)
92 {
93         int lock_fd;
94
95         int lockfile_len = sizeof(addr->sun_path) + sizeof(SOCK_LOCK_SUFFIX);
96         char lockfile[lockfile_len];
97         int len = snprintf(lockfile, lockfile_len, "%s%s", addr->sun_path,
98                            SOCK_LOCK_SUFFIX);
99
100         if (len < 0 || len >= lockfile_len) {
101                 DEBUG_LOG("Can't create socket lock filename\n");
102                 return -1;
103         }
104
105         lock_fd = open(lockfile, O_CREAT | O_RDWR,
106                        S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
107
108         if (lock_fd < 0) {
109                 ERROR_LOG("Cannot access runtime directory\n");
110                 return -1;
111         }
112
113         if (flock(lock_fd, LOCK_EX | LOCK_NB)) {
114                 ERROR_LOG(
115                     "socket %s: in use.  Possible duplicate lease name or "
116                     "mutiple drm-lease-manager instances running\n",
117                     addr->sun_path);
118                 close(lock_fd);
119                 return -1;
120         }
121
122         return lock_fd;
123 }
124
125 static bool server_setup(struct ls *ls, struct ls_server *serv,
126                          struct lease_handle *lease_handle)
127 {
128         struct sockaddr_un *address = &serv->address;
129
130         if (!sockaddr_set_lease_server_path(address, lease_handle->name))
131                 return false;
132
133         int socket_lock = create_socket_lock(address);
134         if (socket_lock < 0)
135                 return false;
136
137         /* The socket address is now owned by this instance, so any existing
138          * sockets can safely be removed */
139         unlink(address->sun_path);
140
141         address->sun_family = AF_UNIX;
142
143         int server_socket = socket(PF_UNIX, SOCK_STREAM | SOCK_NONBLOCK, 0);
144         if (server_socket < 0) {
145                 DEBUG_LOG("Socket creation failed: %s\n", strerror(errno));
146                 return false;
147         }
148
149         if (bind(server_socket, (struct sockaddr *)address, sizeof(*address))) {
150                 ERROR_LOG("Failed to create named socket at %s: %s\n",
151                           address->sun_path, strerror(errno));
152                 close(server_socket);
153                 return false;
154         }
155
156         if (listen(server_socket, 0)) {
157                 DEBUG_LOG("listen failed on %s: %s\n", address->sun_path,
158                           strerror(errno));
159                 close(server_socket);
160                 unlink(address->sun_path);
161                 return false;
162         }
163
164         serv->is_client_connected = false;
165         serv->lease_handle = lease_handle;
166         serv->server_socket_lock = socket_lock;
167         serv->listen.fd = server_socket;
168         serv->listen.serv = serv;
169
170         struct epoll_event ev = {
171             .events = POLLIN,
172             .data.ptr = &serv->listen,
173         };
174
175         if (epoll_ctl(ls->epoll_fd, EPOLL_CTL_ADD, server_socket, &ev)) {
176                 DEBUG_LOG("epoll_ctl add failed: %s\n", strerror(errno));
177                 close(server_socket);
178                 unlink(address->sun_path);
179                 return false;
180         }
181
182         INFO_LOG("Lease server (%s) initialized at %s\n", lease_handle->name,
183                  address->sun_path);
184         return true;
185 }
186
187 static void server_shutdown(struct ls *ls, struct ls_server *serv)
188 {
189         if (unlink(serv->address.sun_path)) {
190                 WARN_LOG("Server socket %s delete failed: %s\n",
191                          serv->address.sun_path, strerror(errno));
192         }
193
194         epoll_ctl(ls->epoll_fd, EPOLL_CTL_DEL, serv->listen.fd, NULL);
195         close(serv->listen.fd);
196         ls_disconnect_client(ls, serv);
197         close(serv->server_socket_lock);
198 }
199
200 struct ls *ls_create(struct lease_handle **lease_handles, int count)
201 {
202         assert(lease_handles);
203         assert(count > 0);
204
205         struct ls *ls = calloc(1, sizeof(struct ls));
206         if (!ls) {
207                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
208                 return NULL;
209         }
210
211         ls->servers = calloc(count, sizeof(struct ls_server));
212         if (!ls->servers) {
213                 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
214                 goto err;
215         }
216
217         ls->epoll_fd = epoll_create1(0);
218         if (ls->epoll_fd < 0) {
219                 DEBUG_LOG("epoll_create failed: %s\n", strerror(errno));
220                 goto err;
221         }
222
223         for (int i = 0; i < count; i++) {
224                 if (!server_setup(ls, &ls->servers[i], lease_handles[i]))
225                         goto err;
226                 ls->nservers++;
227         }
228         return ls;
229 err:
230         ls_destroy(ls);
231         return NULL;
232 }
233
234 void ls_destroy(struct ls *ls)
235 {
236         assert(ls);
237
238         for (int i = 0; i < ls->nservers; i++)
239                 server_shutdown(ls, &ls->servers[i]);
240
241         close(ls->epoll_fd);
242         free(ls->servers);
243         free(ls);
244 }
245
246 bool ls_get_request(struct ls *ls, struct ls_req *req)
247 {
248         assert(ls);
249         assert(req);
250
251         int request = -1;
252         while (request < 0) {
253                 struct epoll_event ev;
254                 if (epoll_wait(ls->epoll_fd, &ev, 1, -1) < 0) {
255                         if (errno == EINTR)
256                                 continue;
257                         DEBUG_LOG("epoll_wait failed: %s\n", strerror(errno));
258                         return false;
259                 }
260
261                 struct ls_socket *sock = ev.data.ptr;
262                 assert(sock);
263
264                 struct ls_server *server = sock->serv;
265                 req->lease_handle = server->lease_handle;
266                 req->server = server;
267
268                 if (sock == &server->listen) {
269                         if (!(ev.events & POLLIN))
270                                 continue;
271                         if (client_connect(ls, server))
272                                 request = LS_REQ_GET_LEASE;
273                 } else if (sock == &server->client) {
274                         if (!(ev.events & POLLHUP))
275                                 continue;
276                         request = LS_REQ_RELEASE_LEASE;
277                 } else {
278                         ERROR_LOG("Internal error: Invalid socket context\n");
279                         return false;
280                 }
281         }
282         req->type = request;
283         return true;
284 }
285
286 bool ls_send_fd(struct ls *ls, struct ls_server *serv, int fd)
287 {
288         assert(ls);
289         assert(serv);
290
291         if (fd < 0)
292                 return false;
293
294         char data[1];
295         struct iovec iov = {
296             .iov_base = data,
297             .iov_len = sizeof(data),
298         };
299
300         char ctrl_buf[CMSG_SPACE(sizeof(int))] = {0};
301
302         struct msghdr msg = {
303             .msg_iov = &iov,
304             .msg_iovlen = 1,
305             .msg_controllen = sizeof(ctrl_buf),
306             .msg_control = ctrl_buf,
307         };
308
309         struct cmsghdr *cmsg = CMSG_FIRSTHDR(&msg);
310         cmsg->cmsg_level = SOL_SOCKET;
311         cmsg->cmsg_type = SCM_RIGHTS;
312         cmsg->cmsg_len = CMSG_LEN(sizeof(int));
313         *((int *)CMSG_DATA(cmsg)) = fd;
314
315         if (sendmsg(serv->client.fd, &msg, 0) < 0) {
316                 DEBUG_LOG("sendmsg failed on %s: %s\n", serv->address.sun_path,
317                           strerror(errno));
318                 return false;
319         }
320
321         INFO_LOG("Lease request granted on %s\n", serv->address.sun_path);
322         return true;
323 }
324
325 void ls_disconnect_client(struct ls *ls, struct ls_server *serv)
326 {
327         assert(ls);
328         assert(serv);
329         if (!serv->is_client_connected)
330                 return;
331
332         epoll_ctl(ls->epoll_fd, EPOLL_CTL_DEL, serv->client.fd, NULL);
333         close(serv->client.fd);
334         serv->is_client_connected = false;
335 }