]> git.proxmox.com Git - mirror_lxcfs.git/blame - utils.c
lxcfs: add --disable-cfs
[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{
99b183fb 160 struct file_info *f;
580fe4df 161
99b183fb 162 f = INTTYPE_TO_PTR(fi->fh);
580fe4df
CB
163 if (!f)
164 return;
165
166 fi->fh = 0;
167
168 free_disarm(f->controller);
169 free_disarm(f->cgroup);
170 free_disarm(f->file);
171 free_disarm(f->buf);
172 free_disarm(f);
173}
1f5596dd
CB
174
175#define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
176
177bool wait_for_sock(int sock, int timeout)
178{
179 struct epoll_event ev;
180 int epfd, ret, now, starttime, deltatime, saved_errno;
181
182 if ((starttime = time(NULL)) < 0)
183 return false;
184
185 if ((epfd = epoll_create(1)) < 0) {
186 lxcfs_error("%s\n", "Failed to create epoll socket: %m.");
187 return false;
188 }
189
190 ev.events = POLLIN_SET;
191 ev.data.fd = sock;
192 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0) {
193 lxcfs_error("%s\n", "Failed adding socket to epoll: %m.");
194 close(epfd);
195 return false;
196 }
197
198again:
199 if ((now = time(NULL)) < 0) {
200 close(epfd);
201 return false;
202 }
203
204 deltatime = (starttime + timeout) - now;
205 if (deltatime < 0) { // timeout
206 errno = 0;
207 close(epfd);
208 return false;
209 }
210 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
211 if (ret < 0 && errno == EINTR)
212 goto again;
213 saved_errno = errno;
214 close(epfd);
215
216 if (ret <= 0) {
217 errno = saved_errno;
218 return false;
219 }
220 return true;
221}
222
223bool recv_creds(int sock, struct ucred *cred, char *v)
224{
225 struct msghdr msg = { 0 };
226 struct iovec iov;
227 struct cmsghdr *cmsg;
228 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
229 char buf[1];
230 int ret;
231 int optval = 1;
232
233 *v = '1';
234
235 cred->pid = -1;
236 cred->uid = -1;
237 cred->gid = -1;
238
239 if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval)) == -1) {
240 lxcfs_error("Failed to set passcred: %s\n", strerror(errno));
241 return false;
242 }
243 buf[0] = '1';
244 if (write(sock, buf, 1) != 1) {
245 lxcfs_error("Failed to start write on scm fd: %s\n", strerror(errno));
246 return false;
247 }
248
249 msg.msg_name = NULL;
250 msg.msg_namelen = 0;
251 msg.msg_control = cmsgbuf;
252 msg.msg_controllen = sizeof(cmsgbuf);
253
254 iov.iov_base = buf;
255 iov.iov_len = sizeof(buf);
256 msg.msg_iov = &iov;
257 msg.msg_iovlen = 1;
258
259 if (!wait_for_sock(sock, 2)) {
260 lxcfs_error("Timed out waiting for scm_cred: %s\n", strerror(errno));
261 return false;
262 }
263 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
264 if (ret < 0) {
265 lxcfs_error("Failed to receive scm_cred: %s\n", strerror(errno));
266 return false;
267 }
268
269 cmsg = CMSG_FIRSTHDR(&msg);
270
271 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(struct ucred)) &&
272 cmsg->cmsg_level == SOL_SOCKET &&
273 cmsg->cmsg_type == SCM_CREDENTIALS) {
274 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
275 }
276 *v = buf[0];
277
278 return true;
279}
280
281static int msgrecv(int sockfd, void *buf, size_t len)
282{
283 if (!wait_for_sock(sockfd, 2))
284 return -1;
285 return recv(sockfd, buf, len, MSG_DONTWAIT);
286}
287
288int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
289{
290 struct msghdr msg = { 0 };
291 struct iovec iov;
292 struct cmsghdr *cmsg;
293 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
294 char buf[1];
295 buf[0] = 'p';
296
297 if (pingfirst) {
298 if (msgrecv(sock, buf, 1) != 1) {
299 lxcfs_error("%s\n", "Error getting reply from server over socketpair.");
300 return SEND_CREDS_FAIL;
301 }
302 }
303
304 msg.msg_control = cmsgbuf;
305 msg.msg_controllen = sizeof(cmsgbuf);
306
307 cmsg = CMSG_FIRSTHDR(&msg);
308 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
309 cmsg->cmsg_level = SOL_SOCKET;
310 cmsg->cmsg_type = SCM_CREDENTIALS;
311 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
312
313 msg.msg_name = NULL;
314 msg.msg_namelen = 0;
315
316 buf[0] = v;
317 iov.iov_base = buf;
318 iov.iov_len = sizeof(buf);
319 msg.msg_iov = &iov;
320 msg.msg_iovlen = 1;
321
322 if (sendmsg(sock, &msg, 0) < 0) {
323 lxcfs_error("Failed at sendmsg: %s.\n",strerror(errno));
324 if (errno == 3)
325 return SEND_CREDS_NOTSK;
326 return SEND_CREDS_FAIL;
327 }
328
329 return SEND_CREDS_OK;
330}
331
332int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d)
333{
334 __do_free char *line = NULL;
335 __do_fclose FILE *f = NULL;
336 size_t linelen = 0, total_len = 0;
337 char *cache = d->buf;
338 size_t cache_size = d->buflen;
339
dbb1f822 340 f = fopen(path, "re");
1f5596dd
CB
341 if (!f)
342 return 0;
343
344 while (getline(&line, &linelen, f) != -1) {
345 ssize_t l = snprintf(cache, cache_size, "%s", line);
346 if (l < 0) {
347 perror("Error writing to cache");
348 return 0;
349 }
350 if (l >= cache_size) {
351 lxcfs_error("%s\n", "Internal error: truncated write to cache.");
352 return 0;
353 }
354 cache += l;
355 cache_size -= l;
356 total_len += l;
357 }
358
359 d->size = total_len;
360 if (total_len > size)
361 total_len = size;
362
363 /* read from off 0 */
364 memcpy(buf, d->buf, total_len);
365
366 if (d->size > total_len)
367 d->cached = d->size - total_len;
368 return total_len;
369}
4ec5c9da
CB
370
371#define INITSCOPE "/init.scope"
372void prune_init_slice(char *cg)
373{
374 char *point;
375 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
376
377 if (cg_len < initscope_len)
378 return;
379
380 point = cg + cg_len - initscope_len;
381 if (strcmp(point, INITSCOPE) == 0) {
382 if (point == cg)
700dd417 383 *(point + 1) = '\0';
4ec5c9da
CB
384 else
385 *point = '\0';
386 }
387}
388
389int wait_for_pid(pid_t pid)
390{
391 int status, ret;
392
393 if (pid <= 0)
394 return -1;
395
396again:
397 ret = waitpid(pid, &status, 0);
398 if (ret == -1) {
399 if (errno == EINTR)
400 goto again;
401 return -1;
402 }
403 if (ret != pid)
404 goto again;
405 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
406 return -1;
407 return 0;
408}
757a63e7
CB
409
410static ssize_t read_nointr(int fd, void *buf, size_t count)
411{
412 ssize_t ret;
413again:
414 ret = read(fd, buf, count);
415 if (ret < 0 && errno == EINTR)
416 goto again;
417
418 return ret;
419}
420
421static void *must_realloc(void *orig, size_t sz)
422{
423 void *ret;
424
425 do {
426 ret = realloc(orig, sz);
427 } while (!ret);
428
429 return ret;
430}
431
432static char *file_to_buf(const char *path, size_t *length)
433{
434 __do_close_prot_errno int fd = -EBADF;
435 __do_free char *copy = NULL;
436 char buf[PATH_MAX];
437
438 if (!length)
439 return NULL;
440
441 fd = open(path, O_RDONLY | O_CLOEXEC);
442 if (fd < 0)
443 return NULL;
444
445 *length = 0;
446 for (;;) {
447 int n;
448 char *old = copy;
449
450 n = read_nointr(fd, buf, sizeof(buf));
451 if (n < 0)
452 return NULL;
453 if (!n)
454 break;
455
456 copy = must_realloc(old, (*length + n) * sizeof(*old));
457 memcpy(copy + *length, buf, n);
458 *length += n;
459 }
460
461 return move_ptr(copy);
462}
463
464FILE *fopen_cached(const char *path, const char *mode, void **caller_freed_buffer)
465{
466 __do_free char *buf = NULL;
467 size_t len = 0;
468 FILE *f;
469
470 buf = file_to_buf(path, &len);
471 if (!buf)
472 return NULL;
473
474 f = fmemopen(buf, len, mode);
475 if (!f)
476 return NULL;
477 *caller_freed_buffer = move_ptr(buf);
478 return f;
479}