1 /* Copyright 2020-2021 IGEL Co., Ltd.
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
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
16 #include "lease-server.h"
18 #include "socket-path.h"
25 #include <sys/epoll.h>
27 #include <sys/socket.h>
29 #include <sys/types.h>
33 #define SOCK_LOCK_SUFFIX ".lock"
37 struct ls_server *serv;
41 struct lease_handle *lease_handle;
42 struct sockaddr_un address;
43 int server_socket_lock;
45 struct ls_socket listen;
46 struct ls_socket client;
48 bool is_client_connected;
54 struct ls_server *servers;
58 static bool client_connect(struct ls *ls, struct ls_server *serv)
60 int cfd = accept(serv->listen.fd, NULL, NULL);
62 DEBUG_LOG("accept failed on %s: %s\n", serv->address.sun_path,
67 if (serv->is_client_connected) {
68 WARN_LOG("Client already connected on %s\n",
69 serv->address.sun_path);
74 serv->client.fd = cfd;
75 serv->client.serv = serv;
77 struct epoll_event ev = {
79 .data.ptr = &serv->client,
81 if (epoll_ctl(ls->epoll_fd, EPOLL_CTL_ADD, cfd, &ev)) {
82 DEBUG_LOG("epoll_ctl add failed: %s\n", strerror(errno));
87 serv->is_client_connected = true;
91 static int create_socket_lock(struct sockaddr_un *addr)
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,
100 if (len < 0 || len >= lockfile_len) {
101 DEBUG_LOG("Can't create socket lock filename\n");
105 lock_fd = open(lockfile, O_CREAT | O_RDWR,
106 S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
109 ERROR_LOG("Cannot access runtime directory\n");
113 if (flock(lock_fd, LOCK_EX | LOCK_NB)) {
115 "socket %s: in use. Possible duplicate lease name or "
116 "mutiple drm-lease-manager instances running\n",
125 static bool server_setup(struct ls *ls, struct ls_server *serv,
126 struct lease_handle *lease_handle)
128 struct sockaddr_un *address = &serv->address;
130 if (!sockaddr_set_lease_server_path(address, lease_handle->name))
133 int socket_lock = create_socket_lock(address);
137 /* The socket address is now owned by this instance, so any existing
138 * sockets can safely be removed */
139 unlink(address->sun_path);
141 address->sun_family = AF_UNIX;
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));
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);
156 if (listen(server_socket, 0)) {
157 DEBUG_LOG("listen failed on %s: %s\n", address->sun_path,
159 close(server_socket);
160 unlink(address->sun_path);
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;
170 struct epoll_event ev = {
172 .data.ptr = &serv->listen,
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);
182 INFO_LOG("Lease server (%s) initialized at %s\n", lease_handle->name,
187 static void server_shutdown(struct ls *ls, struct ls_server *serv)
189 if (unlink(serv->address.sun_path)) {
190 WARN_LOG("Server socket %s delete failed: %s\n",
191 serv->address.sun_path, strerror(errno));
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);
200 struct ls *ls_create(struct lease_handle **lease_handles, int count)
202 assert(lease_handles);
205 struct ls *ls = calloc(1, sizeof(struct ls));
207 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
211 ls->servers = calloc(count, sizeof(struct ls_server));
213 DEBUG_LOG("Memory allocation failed: %s\n", strerror(errno));
217 ls->epoll_fd = epoll_create1(0);
218 if (ls->epoll_fd < 0) {
219 DEBUG_LOG("epoll_create failed: %s\n", strerror(errno));
223 for (int i = 0; i < count; i++) {
224 if (!server_setup(ls, &ls->servers[i], lease_handles[i]))
234 void ls_destroy(struct ls *ls)
238 for (int i = 0; i < ls->nservers; i++)
239 server_shutdown(ls, &ls->servers[i]);
246 bool ls_get_request(struct ls *ls, struct ls_req *req)
252 while (request < 0) {
253 struct epoll_event ev;
254 if (epoll_wait(ls->epoll_fd, &ev, 1, -1) < 0) {
257 DEBUG_LOG("epoll_wait failed: %s\n", strerror(errno));
261 struct ls_socket *sock = ev.data.ptr;
264 struct ls_server *server = sock->serv;
265 req->lease_handle = server->lease_handle;
266 req->server = server;
268 if (sock == &server->listen) {
269 if (!(ev.events & POLLIN))
271 if (client_connect(ls, server))
272 request = LS_REQ_GET_LEASE;
273 } else if (sock == &server->client) {
274 if (!(ev.events & POLLHUP))
276 request = LS_REQ_RELEASE_LEASE;
278 ERROR_LOG("Internal error: Invalid socket context\n");
286 bool ls_send_fd(struct ls *ls, struct ls_server *serv, int fd)
297 .iov_len = sizeof(data),
300 char ctrl_buf[CMSG_SPACE(sizeof(int))] = {0};
302 struct msghdr msg = {
305 .msg_controllen = sizeof(ctrl_buf),
306 .msg_control = ctrl_buf,
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;
315 if (sendmsg(serv->client.fd, &msg, 0) < 0) {
316 DEBUG_LOG("sendmsg failed on %s: %s\n", serv->address.sun_path,
321 INFO_LOG("Lease request granted on %s\n", serv->address.sun_path);
325 void ls_disconnect_client(struct ls *ls, struct ls_server *serv)
329 if (!serv->is_client_connected)
332 epoll_ctl(ls->epoll_fd, EPOLL_CTL_DEL, serv->client.fd, NULL);
333 close(serv->client.fd);
334 serv->is_client_connected = false;