]> git.proxmox.com Git - mirror_lxcfs.git/blame - utils.c
croups: remove unused variable
[mirror_lxcfs.git] / utils.c
CommitLineData
1f5596dd
CB
1/* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3#ifndef _GNU_SOURCE
4#define _GNU_SOURCE
5#endif
6
7#ifndef FUSE_USE_VERSION
8#define FUSE_USE_VERSION 26
9#endif
10
11#define _FILE_OFFSET_BITS 64
12
1d81c6a6
CB
13#include <errno.h>
14#include <fcntl.h>
1f5596dd 15#include <fuse.h>
1d81c6a6
CB
16#include <inttypes.h>
17#include <sched.h>
18#include <stdarg.h>
4ec5c9da 19#include <stdio.h>
1d81c6a6
CB
20#include <stdbool.h>
21#include <stdint.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <string.h>
1f5596dd 25#include <sys/epoll.h>
1d81c6a6
CB
26#include <sys/stat.h>
27#include <sys/types.h>
4ec5c9da 28#include <sys/wait.h>
1d81c6a6
CB
29#include <unistd.h>
30
1f5596dd 31#include "bindings.h"
1d81c6a6
CB
32#include "config.h"
33#include "macro.h"
34#include "memory_utils.h"
35#include "utils.h"
36
37/*
38 * append the given formatted string to *src.
39 * src: a pointer to a char* in which to append the formatted string.
40 * sz: the number of characters printed so far, minus trailing \0.
41 * asz: the allocated size so far
42 * format: string format. See printf for details.
43 * ...: varargs. See printf for details.
44 */
87f7558b
CB
45/*
46 * append the given formatted string to *src.
47 * src: a pointer to a char* in which to append the formatted string.
48 * sz: the number of characters printed so far, minus trailing \0.
49 * asz: the allocated size so far
50 * format: string format. See printf for details.
51 * ...: varargs. See printf for details.
52 */
53char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
1d81c6a6
CB
54{
55 char tmp[BUF_RESERVE_SIZE];
87f7558b
CB
56 va_list args;
57 int tmplen;
1d81c6a6
CB
58
59 va_start (args, format);
87f7558b 60 tmplen = vsnprintf(tmp, BUF_RESERVE_SIZE, format, args);
1d81c6a6
CB
61 va_end(args);
62
63 if (!*src || tmplen + *sz + 1 >= *asz) {
87f7558b 64 char *str;
1d81c6a6 65 do {
87f7558b
CB
66 str = realloc(*src, *asz + BUF_RESERVE_SIZE);
67 } while (!str);
68 *src = str;
1d81c6a6
CB
69 *asz += BUF_RESERVE_SIZE;
70 }
71 memcpy((*src) +*sz , tmp, tmplen+1); /* include the \0 */
72 *sz += tmplen;
87f7558b
CB
73
74 return *src;
1d81c6a6
CB
75}
76
77/**
78 * in_same_namespace - Check whether two processes are in the same namespace.
79 * @pid1 - PID of the first process.
80 * @pid2 - PID of the second process.
81 * @ns - Name of the namespace to check. Must correspond to one of the names
82 * for the namespaces as shown in /proc/<pid/ns/
83 *
84 * If the two processes are not in the same namespace returns an fd to the
85 * namespace of the second process identified by @pid2. If the two processes are
86 * in the same namespace returns -EINVAL, -1 if an error occurred.
87 */
88static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
89{
90 __do_close_prot_errno int ns_fd1 = -1, ns_fd2 = -1;
91 int ret = -1;
92 struct stat ns_st1, ns_st2;
93
94 ns_fd1 = preserve_ns(pid1, ns);
95 if (ns_fd1 < 0) {
96 /* The kernel does not support this namespace. This is not an
97 * error.
98 */
99 if (errno == ENOENT)
100 return -EINVAL;
101
102 return -1;
103 }
104
105 ns_fd2 = preserve_ns(pid2, ns);
106 if (ns_fd2 < 0)
107 return -1;
108
109 ret = fstat(ns_fd1, &ns_st1);
110 if (ret < 0)
111 return -1;
112
113 ret = fstat(ns_fd2, &ns_st2);
114 if (ret < 0)
115 return -1;
116
117 /* processes are in the same namespace */
118 if ((ns_st1.st_dev == ns_st2.st_dev) && (ns_st1.st_ino == ns_st2.st_ino))
119 return -EINVAL;
120
121 /* processes are in different namespaces */
122 return move_fd(ns_fd2);
123}
124
125bool is_shared_pidns(pid_t pid)
126{
127 if (pid != 1)
128 return false;
129
130 if (in_same_namespace(pid, getpid(), "pid") == -EINVAL)
131 return true;
132
133 return false;
134}
135
136int preserve_ns(const int pid, const char *ns)
137{
138 int ret;
139/* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
140#define __NS_PATH_LEN 50
141 char path[__NS_PATH_LEN];
142
143 /* This way we can use this function to also check whether namespaces
144 * are supported by the kernel by passing in the NULL or the empty
145 * string.
146 */
147 ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
148 !ns || strcmp(ns, "") == 0 ? "" : "/",
149 !ns || strcmp(ns, "") == 0 ? "" : ns);
150 if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) {
151 errno = EFBIG;
152 return -1;
153 }
154
155 return open(path, O_RDONLY | O_CLOEXEC);
156}
580fe4df
CB
157
158void do_release_file_info(struct fuse_file_info *fi)
159{
160 struct file_info *f = (struct file_info *)fi->fh;
161
162 if (!f)
163 return;
164
165 fi->fh = 0;
166
167 free_disarm(f->controller);
168 free_disarm(f->cgroup);
169 free_disarm(f->file);
170 free_disarm(f->buf);
171 free_disarm(f);
172}
1f5596dd
CB
173
174#define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
175
176bool wait_for_sock(int sock, int timeout)
177{
178 struct epoll_event ev;
179 int epfd, ret, now, starttime, deltatime, saved_errno;
180
181 if ((starttime = time(NULL)) < 0)
182 return false;
183
184 if ((epfd = epoll_create(1)) < 0) {
185 lxcfs_error("%s\n", "Failed to create epoll socket: %m.");
186 return false;
187 }
188
189 ev.events = POLLIN_SET;
190 ev.data.fd = sock;
191 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
192 lxcfs_error("%s\n", "Failed adding socket to epoll: %m.");
193 close(epfd);
194 return false;
195 }
196
197again:
198 if ((now = time(NULL)) < 0) {
199 close(epfd);
200 return false;
201 }
202
203 deltatime = (starttime + timeout) - now;
204 if (deltatime < 0) { // timeout
205 errno = 0;
206 close(epfd);
207 return false;
208 }
209 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
210 if (ret < 0 && errno == EINTR)
211 goto again;
212 saved_errno = errno;
213 close(epfd);
214
215 if (ret <= 0) {
216 errno = saved_errno;
217 return false;
218 }
219 return true;
220}
221
222bool recv_creds(int sock, struct ucred *cred, char *v)
223{
224 struct msghdr msg = { 0 };
225 struct iovec iov;
226 struct cmsghdr *cmsg;
227 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
228 char buf[1];
229 int ret;
230 int optval = 1;
231
232 *v = '1';
233
234 cred->pid = -1;
235 cred->uid = -1;
236 cred->gid = -1;
237
238 if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
239 lxcfs_error("Failed to set passcred: %s\n", strerror(errno));
240 return false;
241 }
242 buf[0] = '1';
243 if (write(sock, buf, 1) != 1) {
244 lxcfs_error("Failed to start write on scm fd: %s\n", strerror(errno));
245 return false;
246 }
247
248 msg.msg_name = NULL;
249 msg.msg_namelen = 0;
250 msg.msg_control = cmsgbuf;
251 msg.msg_controllen = sizeof(cmsgbuf);
252
253 iov.iov_base = buf;
254 iov.iov_len = sizeof(buf);
255 msg.msg_iov = &iov;
256 msg.msg_iovlen = 1;
257
258 if (!wait_for_sock(sock, 2)) {
259 lxcfs_error("Timed out waiting for scm_cred: %s\n", strerror(errno));
260 return false;
261 }
262 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
263 if (ret < 0) {
264 lxcfs_error("Failed to receive scm_cred: %s\n", strerror(errno));
265 return false;
266 }
267
268 cmsg = CMSG_FIRSTHDR(&msg);
269
270 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
271 cmsg->cmsg_level == SOL_SOCKET &&
272 cmsg->cmsg_type == SCM_CREDENTIALS) {
273 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
274 }
275 *v = buf[0];
276
277 return true;
278}
279
280static int msgrecv(int sockfd, void *buf, size_t len)
281{
282 if (!wait_for_sock(sockfd, 2))
283 return -1;
284 return recv(sockfd, buf, len, MSG_DONTWAIT);
285}
286
287int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
288{
289 struct msghdr msg = { 0 };
290 struct iovec iov;
291 struct cmsghdr *cmsg;
292 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
293 char buf[1];
294 buf[0] = 'p';
295
296 if (pingfirst) {
297 if (msgrecv(sock, buf, 1) != 1) {
298 lxcfs_error("%s\n", "Error getting reply from server over socketpair.");
299 return SEND_CREDS_FAIL;
300 }
301 }
302
303 msg.msg_control = cmsgbuf;
304 msg.msg_controllen = sizeof(cmsgbuf);
305
306 cmsg = CMSG_FIRSTHDR(&msg);
307 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
308 cmsg->cmsg_level = SOL_SOCKET;
309 cmsg->cmsg_type = SCM_CREDENTIALS;
310 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
311
312 msg.msg_name = NULL;
313 msg.msg_namelen = 0;
314
315 buf[0] = v;
316 iov.iov_base = buf;
317 iov.iov_len = sizeof(buf);
318 msg.msg_iov = &iov;
319 msg.msg_iovlen = 1;
320
321 if (sendmsg(sock, &msg, 0) < 0) {
322 lxcfs_error("Failed at sendmsg: %s.\n",strerror(errno));
323 if (errno == 3)
324 return SEND_CREDS_NOTSK;
325 return SEND_CREDS_FAIL;
326 }
327
328 return SEND_CREDS_OK;
329}
330
331int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d)
332{
333 __do_free char *line = NULL;
334 __do_fclose FILE *f = NULL;
335 size_t linelen = 0, total_len = 0;
336 char *cache = d->buf;
337 size_t cache_size = d->buflen;
338
339 f = fopen(path, "r");
340 if (!f)
341 return 0;
342
343 while (getline(&line, &linelen, f) != -1) {
344 ssize_t l = snprintf(cache, cache_size, "%s", line);
345 if (l < 0) {
346 perror("Error writing to cache");
347 return 0;
348 }
349 if (l >= cache_size) {
350 lxcfs_error("%s\n", "Internal error: truncated write to cache.");
351 return 0;
352 }
353 cache += l;
354 cache_size -= l;
355 total_len += l;
356 }
357
358 d->size = total_len;
359 if (total_len > size)
360 total_len = size;
361
362 /* read from off 0 */
363 memcpy(buf, d->buf, total_len);
364
365 if (d->size > total_len)
366 d->cached = d->size - total_len;
367 return total_len;
368}
4ec5c9da
CB
369
370#define INITSCOPE "/init.scope"
371void prune_init_slice(char *cg)
372{
373 char *point;
374 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
375
376 if (cg_len < initscope_len)
377 return;
378
379 point = cg + cg_len - initscope_len;
380 if (strcmp(point, INITSCOPE) == 0) {
381 if (point == cg)
382 *(point+1) = '\0';
383 else
384 *point = '\0';
385 }
386}
387
388int wait_for_pid(pid_t pid)
389{
390 int status, ret;
391
392 if (pid <= 0)
393 return -1;
394
395again:
396 ret = waitpid(pid, &status, 0);
397 if (ret == -1) {
398 if (errno == EINTR)
399 goto again;
400 return -1;
401 }
402 if (ret != pid)
403 goto again;
404 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
405 return -1;
406 return 0;
407}