Add lease request and release protocol
[src/drm-lease-manager.git] / libdlmclient / dlmclient.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 "dlmclient.h"
17
18 #include "dlm-protocol.h"
19 #include "log.h"
20 #include "socket-path.h"
21
22 #include <assert.h>
23 #include <errno.h>
24 #include <stdarg.h>
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/socket.h>
29 #include <sys/un.h>
30 #include <unistd.h>
31
32 void dlm_enable_debug_log(bool enable)
33 {
34         dlm_log_enable_debug(enable);
35 }
36
37 struct dlm_lease {
38         int dlm_server_sock;
39         int lease_fd;
40 };
41
42 static bool lease_connect(struct dlm_lease *lease, const char *name)
43 {
44         struct sockaddr_un sa = {
45             .sun_family = AF_UNIX,
46         };
47
48         if (!sockaddr_set_lease_server_path(&sa, name))
49                 return false;
50
51         int dlm_server_sock = socket(AF_UNIX, SOCK_SEQPACKET, 0);
52         if (dlm_server_sock < 0) {
53                 DEBUG_LOG("Socket creation failed: %s\n", strerror(errno));
54                 return false;
55         }
56
57         while (connect(dlm_server_sock, (struct sockaddr *)&sa,
58                        sizeof(struct sockaddr_un)) == -1) {
59                 if (errno == EINTR)
60                         continue;
61                 DEBUG_LOG("Cannot connect to %s: %s\n", sa.sun_path,
62                           strerror(errno));
63                 close(dlm_server_sock);
64                 return false;
65         }
66         lease->dlm_server_sock = dlm_server_sock;
67         return true;
68 }
69
70 static bool lease_send_request(struct dlm_lease *lease, enum dlm_opcode opcode)
71 {
72         struct dlm_client_request request = {
73             .opcode = opcode,
74         };
75
76         if (!send_dlm_client_request(lease->dlm_server_sock, &request)) {
77                 DEBUG_LOG("Socket data send error: %s\n", strerror(errno));
78                 return false;
79         }
80         return true;
81 }
82
83 static bool lease_recv_fd(struct dlm_lease *lease)
84 {
85         char ctrl_buf[CMSG_SPACE(sizeof(int))] = {0};
86         char data[1] = {0};
87
88         struct iovec iov[1];
89         iov[0].iov_base = data;
90         iov[0].iov_len = sizeof(data);
91
92         struct msghdr msg = {
93             .msg_control = ctrl_buf,
94             .msg_controllen = CMSG_SPACE(sizeof(int)),
95             .msg_iov = iov,
96             .msg_iovlen = 1,
97         };
98
99         int ret;
100         while ((ret = recvmsg(lease->dlm_server_sock, &msg, 0)) <= 0) {
101                 if (ret == 0) {
102                         errno = EACCES;
103                         DEBUG_LOG("Request rejected by DRM lease manager\n");
104                         // TODO: Report why the request was rejected.
105                         return false;
106                 }
107                 if (errno != EINTR) {
108                         DEBUG_LOG("Socket data receive error: %s\n",
109                                   strerror(errno));
110                         return false;
111                 }
112         }
113
114         lease->lease_fd = -1;
115         struct cmsghdr *cmsg;
116         for (cmsg = CMSG_FIRSTHDR(&msg); cmsg != NULL;
117              cmsg = CMSG_NXTHDR(&msg, cmsg)) {
118                 if (cmsg->cmsg_level == SOL_SOCKET &&
119                     cmsg->cmsg_type == SCM_RIGHTS) {
120                         int nfds = (cmsg->cmsg_len - CMSG_LEN(0)) / sizeof(int);
121                         int *fds = (int *)CMSG_DATA(cmsg);
122
123                         if (nfds == 1) {
124                                 lease->lease_fd = fds[0];
125                                 break;
126                         }
127
128                         DEBUG_LOG(
129                             "Expected 1 fd from lease manager. Received %d\n",
130                             nfds);
131                         /* Close any unexpected fds so we don't leak them. */
132                         for (int i = 0; i < nfds; i++)
133                                 close(fds[i]);
134                         break;
135                 }
136         }
137
138         if (lease->lease_fd < 0) {
139                 DEBUG_LOG("Expected data not received from lease manager\n");
140                 errno = EPROTO;
141                 return false;
142         }
143
144         return true;
145 }
146
147 struct dlm_lease *dlm_get_lease(const char *name)
148 {
149         int saved_errno;
150         struct dlm_lease *lease = calloc(1, sizeof(struct dlm_lease));
151         if (!lease) {
152                 DEBUG_LOG("can't allocate memory : %s\n", strerror(errno));
153                 return NULL;
154         }
155
156         if (!lease_connect(lease, name)) {
157                 free(lease);
158                 return NULL;
159         }
160
161         if (!lease_send_request(lease, DLM_GET_LEASE))
162                 goto err;
163
164         if (!lease_recv_fd(lease))
165                 goto err;
166
167         return lease;
168
169 err:
170         saved_errno = errno;
171         dlm_release_lease(lease);
172         errno = saved_errno;
173         return NULL;
174 }
175
176 void dlm_release_lease(struct dlm_lease *lease)
177 {
178         if (!lease)
179                 return;
180
181         lease_send_request(lease, DLM_RELEASE_LEASE);
182         close(lease->lease_fd);
183         close(lease->dlm_server_sock);
184         free(lease);
185 }
186
187 int dlm_lease_fd(struct dlm_lease *lease)
188 {
189         if (!lease)
190                 return -1;
191
192         return lease->lease_fd;
193 }