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