]> git.proxmox.com Git - mirror_lxcfs.git/blame - src/utils.c
utils: add get_task_personality helper
[mirror_lxcfs.git] / src / utils.c
CommitLineData
db0463bf 1/* SPDX-License-Identifier: LGPL-2.1+ */
1f5596dd 2
f834b6bf
SP
3#include "config.h"
4
b0f33646 5#include <ctype.h>
1d81c6a6
CB
6#include <errno.h>
7#include <fcntl.h>
8#include <inttypes.h>
9#include <sched.h>
10#include <stdarg.h>
11#include <stdbool.h>
12#include <stdint.h>
13#include <stdio.h>
14#include <stdlib.h>
15#include <string.h>
1f5596dd 16#include <sys/epoll.h>
1d81c6a6
CB
17#include <sys/stat.h>
18#include <sys/types.h>
4ec5c9da 19#include <sys/wait.h>
1d81c6a6
CB
20#include <unistd.h>
21
e01afbb7
CB
22#include "utils.h"
23
1f5596dd 24#include "bindings.h"
1d81c6a6
CB
25#include "macro.h"
26#include "memory_utils.h"
1d81c6a6
CB
27
28/*
29 * append the given formatted string to *src.
30 * src: a pointer to a char* in which to append the formatted string.
31 * sz: the number of characters printed so far, minus trailing \0.
32 * asz: the allocated size so far
33 * format: string format. See printf for details.
34 * ...: varargs. See printf for details.
35 */
87f7558b 36char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
1d81c6a6
CB
37{
38 char tmp[BUF_RESERVE_SIZE];
87f7558b
CB
39 va_list args;
40 int tmplen;
1d81c6a6
CB
41
42 va_start (args, format);
87f7558b 43 tmplen = vsnprintf(tmp, BUF_RESERVE_SIZE, format, args);
1d81c6a6
CB
44 va_end(args);
45
46 if (!*src || tmplen + *sz + 1 >= *asz) {
87f7558b 47 char *str;
1d81c6a6 48 do {
87f7558b
CB
49 str = realloc(*src, *asz + BUF_RESERVE_SIZE);
50 } while (!str);
51 *src = str;
1d81c6a6
CB
52 *asz += BUF_RESERVE_SIZE;
53 }
54 memcpy((*src) +*sz , tmp, tmplen+1); /* include the \0 */
55 *sz += tmplen;
87f7558b
CB
56
57 return *src;
1d81c6a6
CB
58}
59
60/**
61 * in_same_namespace - Check whether two processes are in the same namespace.
62 * @pid1 - PID of the first process.
63 * @pid2 - PID of the second process.
64 * @ns - Name of the namespace to check. Must correspond to one of the names
65 * for the namespaces as shown in /proc/<pid/ns/
66 *
67 * If the two processes are not in the same namespace returns an fd to the
68 * namespace of the second process identified by @pid2. If the two processes are
69 * in the same namespace returns -EINVAL, -1 if an error occurred.
70 */
71static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
72{
05b7a16d 73 __do_close int ns_fd1 = -1, ns_fd2 = -1;
1d81c6a6
CB
74 int ret = -1;
75 struct stat ns_st1, ns_st2;
76
77 ns_fd1 = preserve_ns(pid1, ns);
78 if (ns_fd1 < 0) {
79 /* The kernel does not support this namespace. This is not an
80 * error.
81 */
82 if (errno == ENOENT)
83 return -EINVAL;
84
85 return -1;
86 }
87
88 ns_fd2 = preserve_ns(pid2, ns);
89 if (ns_fd2 < 0)
90 return -1;
91
92 ret = fstat(ns_fd1, &ns_st1);
93 if (ret < 0)
94 return -1;
95
96 ret = fstat(ns_fd2, &ns_st2);
97 if (ret < 0)
98 return -1;
99
100 /* processes are in the same namespace */
101 if ((ns_st1.st_dev == ns_st2.st_dev) && (ns_st1.st_ino == ns_st2.st_ino))
102 return -EINVAL;
103
104 /* processes are in different namespaces */
105 return move_fd(ns_fd2);
106}
107
108bool is_shared_pidns(pid_t pid)
109{
802b0d20
CB
110 __do_close int fd = -EBADF;
111
1d81c6a6
CB
112 if (pid != 1)
113 return false;
114
802b0d20
CB
115 fd = in_same_namespace(pid, getpid(), "pid");
116 if (fd == EINVAL)
1d81c6a6
CB
117 return true;
118
119 return false;
120}
121
122int preserve_ns(const int pid, const char *ns)
123{
124 int ret;
125/* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
126#define __NS_PATH_LEN 50
127 char path[__NS_PATH_LEN];
128
129 /* This way we can use this function to also check whether namespaces
130 * are supported by the kernel by passing in the NULL or the empty
131 * string.
132 */
133 ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
134 !ns || strcmp(ns, "") == 0 ? "" : "/",
135 !ns || strcmp(ns, "") == 0 ? "" : ns);
136 if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) {
137 errno = EFBIG;
138 return -1;
139 }
140
141 return open(path, O_RDONLY | O_CLOEXEC);
142}
580fe4df
CB
143
144void do_release_file_info(struct fuse_file_info *fi)
145{
99b183fb 146 struct file_info *f;
580fe4df 147
99b183fb 148 f = INTTYPE_TO_PTR(fi->fh);
580fe4df
CB
149 if (!f)
150 return;
151
152 fi->fh = 0;
153
154 free_disarm(f->controller);
155 free_disarm(f->cgroup);
156 free_disarm(f->file);
157 free_disarm(f->buf);
158 free_disarm(f);
159}
1f5596dd
CB
160
161#define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
162
163bool wait_for_sock(int sock, int timeout)
164{
05b7a16d 165 __do_close int epfd = -EBADF;
1f5596dd 166 struct epoll_event ev;
915c14d0 167 int ret, now, starttime, deltatime;
1f5596dd
CB
168
169 if ((starttime = time(NULL)) < 0)
170 return false;
171
41eb015d
CB
172 epfd = epoll_create(1);
173 if (epfd < 0)
e4bd0d4d 174 return log_error(false, "%m - Failed to create epoll socket");
1f5596dd
CB
175
176 ev.events = POLLIN_SET;
177 ev.data.fd = sock;
41eb015d
CB
178 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0)
179 return log_error(false, "Failed adding socket to epoll: %m");
1f5596dd
CB
180
181again:
41eb015d 182 if ((now = time(NULL)) < 0)
1f5596dd 183 return false;
1f5596dd
CB
184
185 deltatime = (starttime + timeout) - now;
41eb015d 186 if (deltatime < 0)
1f5596dd 187 return false;
41eb015d 188
1f5596dd
CB
189 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
190 if (ret < 0 && errno == EINTR)
191 goto again;
1f5596dd 192
41eb015d 193 if (ret <= 0)
1f5596dd 194 return false;
41eb015d 195
1f5596dd
CB
196 return true;
197}
198
199bool recv_creds(int sock, struct ucred *cred, char *v)
200{
dac3dc93 201 struct msghdr msg = {};
1f5596dd
CB
202 struct iovec iov;
203 struct cmsghdr *cmsg;
dac3dc93
CB
204 ssize_t ret;
205 char cmsgbuf[CMSG_SPACE(sizeof(*cred))] = {};
206 char buf = '1';
1f5596dd
CB
207 int optval = 1;
208
1f5596dd
CB
209 msg.msg_name = NULL;
210 msg.msg_namelen = 0;
211 msg.msg_control = cmsgbuf;
212 msg.msg_controllen = sizeof(cmsgbuf);
213
dac3dc93 214 iov.iov_base = &buf;
1f5596dd
CB
215 iov.iov_len = sizeof(buf);
216 msg.msg_iov = &iov;
217 msg.msg_iovlen = 1;
218
dac3dc93
CB
219 *v = buf;
220
221 ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval));
222 if (ret < 0)
223 return log_error(false, "Failed to set passcred: %s\n", strerror(errno));
224
225 ret = write_nointr(sock, &buf, sizeof(buf));
226 if (ret != sizeof(buf))
227 return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno));
228
41eb015d
CB
229 if (!wait_for_sock(sock, 2))
230 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno));
231
1f5596dd 232 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
41eb015d
CB
233 if (ret < 0)
234 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno));
1f5596dd
CB
235
236 cmsg = CMSG_FIRSTHDR(&msg);
237
dac3dc93
CB
238 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(*cred)) &&
239 cmsg->cmsg_level == SOL_SOCKET &&
240 cmsg->cmsg_type == SCM_CREDENTIALS) {
1f5596dd
CB
241 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
242 }
dac3dc93 243 *v = buf;
1f5596dd
CB
244
245 return true;
246}
247
248static int msgrecv(int sockfd, void *buf, size_t len)
249{
250 if (!wait_for_sock(sockfd, 2))
251 return -1;
41eb015d 252
1f5596dd
CB
253 return recv(sockfd, buf, len, MSG_DONTWAIT);
254}
255
256int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
257{
258 struct msghdr msg = { 0 };
259 struct iovec iov;
260 struct cmsghdr *cmsg;
261 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
262 char buf[1];
263 buf[0] = 'p';
264
41eb015d
CB
265 if (pingfirst && msgrecv(sock, buf, 1) != 1)
266 return log_error(SEND_CREDS_FAIL, "%s - Failed getting reply from server over socketpair: %d",
267 strerror(errno), SEND_CREDS_FAIL);
1f5596dd
CB
268
269 msg.msg_control = cmsgbuf;
270 msg.msg_controllen = sizeof(cmsgbuf);
271
272 cmsg = CMSG_FIRSTHDR(&msg);
273 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
274 cmsg->cmsg_level = SOL_SOCKET;
275 cmsg->cmsg_type = SCM_CREDENTIALS;
276 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
277
278 msg.msg_name = NULL;
279 msg.msg_namelen = 0;
280
281 buf[0] = v;
282 iov.iov_base = buf;
283 iov.iov_len = sizeof(buf);
284 msg.msg_iov = &iov;
285 msg.msg_iovlen = 1;
286
287 if (sendmsg(sock, &msg, 0) < 0) {
1f5596dd 288 if (errno == 3)
41eb015d
CB
289 return log_error(SEND_CREDS_NOTSK, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_NOTSK);
290
291 return log_error(SEND_CREDS_FAIL, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_FAIL);
1f5596dd
CB
292 }
293
294 return SEND_CREDS_OK;
295}
296
297int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d)
298{
299 __do_free char *line = NULL;
300 __do_fclose FILE *f = NULL;
301 size_t linelen = 0, total_len = 0;
302 char *cache = d->buf;
303 size_t cache_size = d->buflen;
304
dbb1f822 305 f = fopen(path, "re");
1f5596dd
CB
306 if (!f)
307 return 0;
308
309 while (getline(&line, &linelen, f) != -1) {
3cf1e562
CB
310 ssize_t l;
311
312 l = snprintf(cache, cache_size, "%s", line);
41eb015d
CB
313 if (l < 0)
314 return log_error(0, "Failed to write cache");
3cf1e562 315 if ((size_t)l >= cache_size)
41eb015d
CB
316 return log_error(0, "Write to cache was truncated");
317
1f5596dd
CB
318 cache += l;
319 cache_size -= l;
320 total_len += l;
321 }
322
323 d->size = total_len;
324 if (total_len > size)
325 total_len = size;
326
327 /* read from off 0 */
328 memcpy(buf, d->buf, total_len);
329
3cf1e562 330 if (d->size > (int)total_len)
1f5596dd 331 d->cached = d->size - total_len;
cbfc55fd 332
1f5596dd
CB
333 return total_len;
334}
4ec5c9da 335
cbfc55fd
CB
336int read_file_fuse_with_offset(const char *path, char *buf, size_t size,
337 off_t offset, struct file_info *d)
338{
339 if (offset) {
340 ssize_t total_len = 0;
341 char *cache = d->buf;
3cf1e562 342 size_t left;
cbfc55fd
CB
343
344 if (offset > d->size)
345 return -EINVAL;
346
347 if (!d->cached)
348 return 0;
349
350 left = d->size - offset;
351 total_len = left > size ? size : left;
352 memcpy(buf, cache + offset, total_len);
353
354 return total_len;
355 }
356
357 return read_file_fuse(path, buf, size, d);
358}
359
4ec5c9da
CB
360#define INITSCOPE "/init.scope"
361void prune_init_slice(char *cg)
362{
363 char *point;
364 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
365
366 if (cg_len < initscope_len)
367 return;
368
369 point = cg + cg_len - initscope_len;
370 if (strcmp(point, INITSCOPE) == 0) {
371 if (point == cg)
700dd417 372 *(point + 1) = '\0';
4ec5c9da
CB
373 else
374 *point = '\0';
375 }
376}
377
378int wait_for_pid(pid_t pid)
379{
380 int status, ret;
381
382 if (pid <= 0)
383 return -1;
384
385again:
386 ret = waitpid(pid, &status, 0);
387 if (ret == -1) {
388 if (errno == EINTR)
389 goto again;
390 return -1;
391 }
392 if (ret != pid)
393 goto again;
394 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
395 return -1;
396 return 0;
397}
757a63e7
CB
398
399static ssize_t read_nointr(int fd, void *buf, size_t count)
400{
401 ssize_t ret;
402again:
403 ret = read(fd, buf, count);
404 if (ret < 0 && errno == EINTR)
405 goto again;
406
407 return ret;
408}
409
410static void *must_realloc(void *orig, size_t sz)
411{
412 void *ret;
413
414 do {
415 ret = realloc(orig, sz);
416 } while (!ret);
417
418 return ret;
419}
420
28519477 421static char *fd_to_buf(int fd, size_t *length)
757a63e7 422{
757a63e7 423 __do_free char *copy = NULL;
757a63e7
CB
424
425 if (!length)
426 return NULL;
427
757a63e7
CB
428 *length = 0;
429 for (;;) {
28519477
CB
430 ssize_t bytes_read;
431 char buf[4096];
757a63e7
CB
432 char *old = copy;
433
28519477
CB
434 bytes_read = read_nointr(fd, buf, sizeof(buf));
435 if (bytes_read < 0)
757a63e7 436 return NULL;
28519477
CB
437
438 if (!bytes_read)
757a63e7
CB
439 break;
440
28519477
CB
441 copy = must_realloc(old, (*length + bytes_read) * sizeof(*old));
442 memcpy(copy + *length, buf, bytes_read);
443 *length += bytes_read;
757a63e7
CB
444 }
445
446 return move_ptr(copy);
447}
448
28519477
CB
449static char *file_to_buf(const char *path, size_t *length)
450{
05b7a16d 451 __do_close int fd = -EBADF;
28519477
CB
452
453 if (!length)
454 return NULL;
455
456 fd = open(path, O_RDONLY | O_CLOEXEC);
457 if (fd < 0)
458 return NULL;
459
460 return fd_to_buf(fd, length);
461}
462
757a63e7
CB
463FILE *fopen_cached(const char *path, const char *mode, void **caller_freed_buffer)
464{
465 __do_free char *buf = NULL;
466 size_t len = 0;
467 FILE *f;
468
469 buf = file_to_buf(path, &len);
470 if (!buf)
471 return NULL;
472
473 f = fmemopen(buf, len, mode);
474 if (!f)
475 return NULL;
476 *caller_freed_buffer = move_ptr(buf);
477 return f;
478}
28519477 479
28519477
CB
480FILE *fdopen_cached(int fd, const char *mode, void **caller_freed_buffer)
481{
482 __do_free char *buf = NULL;
483 size_t len = 0;
484 FILE *f;
485
486 buf = fd_to_buf(fd, &len);
487 if (!buf)
488 return NULL;
489
490 f = fmemopen(buf, len, mode);
491 if (!f)
492 return NULL;
493
494 *caller_freed_buffer = move_ptr(buf);
495 return f;
496}
2f543378
CB
497
498ssize_t write_nointr(int fd, const void *buf, size_t count)
499{
500 ssize_t ret;
501
502 do {
503 ret = write(fd, buf, count);
504 } while (ret < 0 && errno == EINTR);
505
506 return ret;
507}
b0f33646
CB
508
509int safe_uint64(const char *numstr, uint64_t *converted, int base)
510{
511 char *err = NULL;
512 uint64_t u;
513
514 while (isspace(*numstr))
515 numstr++;
516
517 if (*numstr == '-')
518 return -EINVAL;
519
520 errno = 0;
521 u = strtoull(numstr, &err, base);
522 if (errno == ERANGE && u == UINT64_MAX)
523 return -ERANGE;
524
525 if (err == numstr || *err != '\0')
526 return -EINVAL;
527
528 *converted = u;
529 return 0;
530}
8b6987a2 531
649e9ccc
AM
532int safe_uint32(const char *numstr, uint32_t *converted, int base)
533{
534 char *err = NULL;
535 unsigned long uli;
536
537 while (isspace(*numstr))
538 numstr++;
539
540 if (*numstr == '-')
541 return -EINVAL;
542
543 errno = 0;
544 uli = strtoul(numstr, &err, base);
545 if (errno == ERANGE && uli == UINT32_MAX)
546 return -ERANGE;
547
548 if (err == numstr || *err != '\0')
549 return -EINVAL;
550
551 *converted = (uint32_t)uli;
552 return 0;
553}
554
8b6987a2
CB
555static int char_left_gc(const char *buffer, size_t len)
556{
557 size_t i;
558
559 for (i = 0; i < len; i++) {
560 if (buffer[i] == ' ' ||
561 buffer[i] == '\t')
562 continue;
563
564 return i;
565 }
566
567 return 0;
568}
569
570static int char_right_gc(const char *buffer, size_t len)
571{
572 int i;
573
574 for (i = len - 1; i >= 0; i--) {
575 if (buffer[i] == ' ' ||
576 buffer[i] == '\t' ||
577 buffer[i] == '\n' ||
578 buffer[i] == '\0')
579 continue;
580
581 return i + 1;
582 }
583
584 return 0;
585}
586
587char *trim_whitespace_in_place(char *buffer)
588{
589 buffer += char_left_gc(buffer, strlen(buffer));
590 buffer[char_right_gc(buffer, strlen(buffer))] = '\0';
591 return buffer;
592}
a3c8d33c
CB
593
594#define BATCH_SIZE 50
595static int batch_realloc(char **mem, size_t oldlen, size_t newlen)
596{
597 int newbatches = (newlen / BATCH_SIZE) + 1;
598 int oldbatches = (oldlen / BATCH_SIZE) + 1;
599
600 if (!*mem || newbatches > oldbatches) {
601 char *tmp;
602
603 tmp = realloc(*mem, newbatches * BATCH_SIZE);
604 if (!tmp)
605 return -ENOMEM;
606 *mem = tmp;
607 }
608
609 return 0;
610}
611
612static int append_line(char **dest, size_t oldlen, char *new, size_t newlen)
613{
614 int ret;
615 size_t full = oldlen + newlen;
616
617 ret = batch_realloc(dest, oldlen, full + 1);
618 if (ret)
619 return ret;
620
621 memcpy(*dest + oldlen, new, newlen + 1);
622 return 0;
623}
624
625/* Slurp in a whole file */
626char *read_file_at(int dfd, const char *fnam, unsigned int o_flags)
627{
628 __do_close int fd = -EBADF;
629 __do_free char *buf = NULL, *line = NULL;
630 __do_fclose FILE *f = NULL;
631 size_t len = 0, fulllen = 0;
632 int linelen;
633
634 fd = openat(dfd, fnam, o_flags, 0);
635 if (fd < 0)
636 return NULL;
637
638 f = fdopen(fd, "re");
639 if (!f)
640 return NULL;
641 /* Transfer ownership to fdopen(). */
642 move_fd(fd);
643
644 while ((linelen = getline(&line, &len, f)) != -1) {
645 if (append_line(&buf, fulllen, line, linelen))
646 return NULL;
647 fulllen += linelen;
648 }
649
650 return move_ptr(buf);
651}
a6fd03eb 652
f5227e5c 653DIR *opendir_flags(const char *path, int flags)
a6fd03eb
CBM
654{
655 __do_close int dfd = -EBADF;
656 DIR *dirp;
657
f5227e5c 658 dfd = open(path, O_DIRECTORY | flags);
a6fd03eb
CBM
659 if (dfd < 0)
660 return NULL;
661
662 dirp = fdopendir(dfd);
663 if (dirp)
664 move_fd(dfd); /* Transfer ownership to fdopendir(). */
665
666 return dirp;
667}
474491c4
AM
668
669int get_task_personality(pid_t pid, __u32 *personality)
670{
671 __do_close int fd = -EBADF;
672 int ret = -1;
673 char path[STRLITERALLEN("/proc//personality") + INTTYPE_TO_STRLEN(pid_t) + 1];
674 /* seq_printf(m, "%08x\n", task->personality); */
675 char buf[8 + 1];
676
677 ret = strnprintf(path, sizeof(path), "/proc/%d/personality", pid);
678 if (ret < 0)
679 return -1;
680
681 fd = open(path, O_RDONLY | O_CLOEXEC);
682 if (fd < 0)
683 return -1;
684
685 ret = read_nointr(fd, buf, sizeof(buf) - 1);
686 if (ret >= 0) {
687 buf[ret] = '\0';
688 if (safe_uint32(buf, personality, 16) < 0)
689 return log_error(-1, "Failed to convert personality %s", buf);
690 }
691
692 return ret;
693}