]> git.proxmox.com Git - mirror_lxcfs.git/blob - src/utils.c
tree-wide: set _GNU_SOURCE in meson.build
[mirror_lxcfs.git] / src / utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include "config.h"
4
5 #include <ctype.h>
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>
16 #include <sys/epoll.h>
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <sys/wait.h>
20 #include <unistd.h>
21
22 #include "utils.h"
23
24 #include "bindings.h"
25 #include "macro.h"
26 #include "memory_utils.h"
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 */
36 /*
37 * append the given formatted string to *src.
38 * src: a pointer to a char* in which to append the formatted string.
39 * sz: the number of characters printed so far, minus trailing \0.
40 * asz: the allocated size so far
41 * format: string format. See printf for details.
42 * ...: varargs. See printf for details.
43 */
44 char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
45 {
46 char tmp[BUF_RESERVE_SIZE];
47 va_list args;
48 int tmplen;
49
50 va_start (args, format);
51 tmplen = vsnprintf(tmp, BUF_RESERVE_SIZE, format, args);
52 va_end(args);
53
54 if (!*src || tmplen + *sz + 1 >= *asz) {
55 char *str;
56 do {
57 str = realloc(*src, *asz + BUF_RESERVE_SIZE);
58 } while (!str);
59 *src = str;
60 *asz += BUF_RESERVE_SIZE;
61 }
62 memcpy((*src) +*sz , tmp, tmplen+1); /* include the \0 */
63 *sz += tmplen;
64
65 return *src;
66 }
67
68 /**
69 * in_same_namespace - Check whether two processes are in the same namespace.
70 * @pid1 - PID of the first process.
71 * @pid2 - PID of the second process.
72 * @ns - Name of the namespace to check. Must correspond to one of the names
73 * for the namespaces as shown in /proc/<pid/ns/
74 *
75 * If the two processes are not in the same namespace returns an fd to the
76 * namespace of the second process identified by @pid2. If the two processes are
77 * in the same namespace returns -EINVAL, -1 if an error occurred.
78 */
79 static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
80 {
81 __do_close int ns_fd1 = -1, ns_fd2 = -1;
82 int ret = -1;
83 struct stat ns_st1, ns_st2;
84
85 ns_fd1 = preserve_ns(pid1, ns);
86 if (ns_fd1 < 0) {
87 /* The kernel does not support this namespace. This is not an
88 * error.
89 */
90 if (errno == ENOENT)
91 return -EINVAL;
92
93 return -1;
94 }
95
96 ns_fd2 = preserve_ns(pid2, ns);
97 if (ns_fd2 < 0)
98 return -1;
99
100 ret = fstat(ns_fd1, &ns_st1);
101 if (ret < 0)
102 return -1;
103
104 ret = fstat(ns_fd2, &ns_st2);
105 if (ret < 0)
106 return -1;
107
108 /* processes are in the same namespace */
109 if ((ns_st1.st_dev == ns_st2.st_dev) && (ns_st1.st_ino == ns_st2.st_ino))
110 return -EINVAL;
111
112 /* processes are in different namespaces */
113 return move_fd(ns_fd2);
114 }
115
116 bool is_shared_pidns(pid_t pid)
117 {
118 __do_close int fd = -EBADF;
119
120 if (pid != 1)
121 return false;
122
123 fd = in_same_namespace(pid, getpid(), "pid");
124 if (fd == EINVAL)
125 return true;
126
127 return false;
128 }
129
130 int preserve_ns(const int pid, const char *ns)
131 {
132 int ret;
133 /* 5 /proc + 21 /int_as_str + 3 /ns + 20 /NS_NAME + 1 \0 */
134 #define __NS_PATH_LEN 50
135 char path[__NS_PATH_LEN];
136
137 /* This way we can use this function to also check whether namespaces
138 * are supported by the kernel by passing in the NULL or the empty
139 * string.
140 */
141 ret = snprintf(path, __NS_PATH_LEN, "/proc/%d/ns%s%s", pid,
142 !ns || strcmp(ns, "") == 0 ? "" : "/",
143 !ns || strcmp(ns, "") == 0 ? "" : ns);
144 if (ret < 0 || (size_t)ret >= __NS_PATH_LEN) {
145 errno = EFBIG;
146 return -1;
147 }
148
149 return open(path, O_RDONLY | O_CLOEXEC);
150 }
151
152 void do_release_file_info(struct fuse_file_info *fi)
153 {
154 struct file_info *f;
155
156 f = INTTYPE_TO_PTR(fi->fh);
157 if (!f)
158 return;
159
160 fi->fh = 0;
161
162 free_disarm(f->controller);
163 free_disarm(f->cgroup);
164 free_disarm(f->file);
165 free_disarm(f->buf);
166 free_disarm(f);
167 }
168
169 #define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
170
171 bool wait_for_sock(int sock, int timeout)
172 {
173 __do_close int epfd = -EBADF;
174 struct epoll_event ev;
175 int ret, now, starttime, deltatime;
176
177 if ((starttime = time(NULL)) < 0)
178 return false;
179
180 epfd = epoll_create(1);
181 if (epfd < 0)
182 return log_error(false, "%m - Failed to create epoll socket");
183
184 ev.events = POLLIN_SET;
185 ev.data.fd = sock;
186 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0)
187 return log_error(false, "Failed adding socket to epoll: %m");
188
189 again:
190 if ((now = time(NULL)) < 0)
191 return false;
192
193 deltatime = (starttime + timeout) - now;
194 if (deltatime < 0)
195 return false;
196
197 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
198 if (ret < 0 && errno == EINTR)
199 goto again;
200
201 if (ret <= 0)
202 return false;
203
204 return true;
205 }
206
207 bool recv_creds(int sock, struct ucred *cred, char *v)
208 {
209 struct msghdr msg = {};
210 struct iovec iov;
211 struct cmsghdr *cmsg;
212 ssize_t ret;
213 char cmsgbuf[CMSG_SPACE(sizeof(*cred))] = {};
214 char buf = '1';
215 int optval = 1;
216
217 msg.msg_name = NULL;
218 msg.msg_namelen = 0;
219 msg.msg_control = cmsgbuf;
220 msg.msg_controllen = sizeof(cmsgbuf);
221
222 iov.iov_base = &buf;
223 iov.iov_len = sizeof(buf);
224 msg.msg_iov = &iov;
225 msg.msg_iovlen = 1;
226
227 *v = buf;
228
229 ret = setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &optval, sizeof(optval));
230 if (ret < 0)
231 return log_error(false, "Failed to set passcred: %s\n", strerror(errno));
232
233 ret = write_nointr(sock, &buf, sizeof(buf));
234 if (ret != sizeof(buf))
235 return log_error(false, "Failed to start write on scm fd: %s\n", strerror(errno));
236
237 if (!wait_for_sock(sock, 2))
238 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno));
239
240 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
241 if (ret < 0)
242 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno));
243
244 cmsg = CMSG_FIRSTHDR(&msg);
245
246 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(*cred)) &&
247 cmsg->cmsg_level == SOL_SOCKET &&
248 cmsg->cmsg_type == SCM_CREDENTIALS) {
249 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
250 }
251 *v = buf;
252
253 return true;
254 }
255
256 static int msgrecv(int sockfd, void *buf, size_t len)
257 {
258 if (!wait_for_sock(sockfd, 2))
259 return -1;
260
261 return recv(sockfd, buf, len, MSG_DONTWAIT);
262 }
263
264 int send_creds(int sock, struct ucred *cred, char v, bool pingfirst)
265 {
266 struct msghdr msg = { 0 };
267 struct iovec iov;
268 struct cmsghdr *cmsg;
269 char cmsgbuf[CMSG_SPACE(sizeof(*cred))];
270 char buf[1];
271 buf[0] = 'p';
272
273 if (pingfirst && msgrecv(sock, buf, 1) != 1)
274 return log_error(SEND_CREDS_FAIL, "%s - Failed getting reply from server over socketpair: %d",
275 strerror(errno), SEND_CREDS_FAIL);
276
277 msg.msg_control = cmsgbuf;
278 msg.msg_controllen = sizeof(cmsgbuf);
279
280 cmsg = CMSG_FIRSTHDR(&msg);
281 cmsg->cmsg_len = CMSG_LEN(sizeof(struct ucred));
282 cmsg->cmsg_level = SOL_SOCKET;
283 cmsg->cmsg_type = SCM_CREDENTIALS;
284 memcpy(CMSG_DATA(cmsg), cred, sizeof(*cred));
285
286 msg.msg_name = NULL;
287 msg.msg_namelen = 0;
288
289 buf[0] = v;
290 iov.iov_base = buf;
291 iov.iov_len = sizeof(buf);
292 msg.msg_iov = &iov;
293 msg.msg_iovlen = 1;
294
295 if (sendmsg(sock, &msg, 0) < 0) {
296 if (errno == 3)
297 return log_error(SEND_CREDS_NOTSK, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_NOTSK);
298
299 return log_error(SEND_CREDS_FAIL, "%s - Failed at sendmsg: %d", strerror(errno), SEND_CREDS_FAIL);
300 }
301
302 return SEND_CREDS_OK;
303 }
304
305 int read_file_fuse(const char *path, char *buf, size_t size, struct file_info *d)
306 {
307 __do_free char *line = NULL;
308 __do_fclose FILE *f = NULL;
309 size_t linelen = 0, total_len = 0;
310 char *cache = d->buf;
311 size_t cache_size = d->buflen;
312
313 f = fopen(path, "re");
314 if (!f)
315 return 0;
316
317 while (getline(&line, &linelen, f) != -1) {
318 ssize_t l;
319
320 l = snprintf(cache, cache_size, "%s", line);
321 if (l < 0)
322 return log_error(0, "Failed to write cache");
323 if ((size_t)l >= cache_size)
324 return log_error(0, "Write to cache was truncated");
325
326 cache += l;
327 cache_size -= l;
328 total_len += l;
329 }
330
331 d->size = total_len;
332 if (total_len > size)
333 total_len = size;
334
335 /* read from off 0 */
336 memcpy(buf, d->buf, total_len);
337
338 if (d->size > (int)total_len)
339 d->cached = d->size - total_len;
340
341 return total_len;
342 }
343
344 int read_file_fuse_with_offset(const char *path, char *buf, size_t size,
345 off_t offset, struct file_info *d)
346 {
347 if (offset) {
348 ssize_t total_len = 0;
349 char *cache = d->buf;
350 size_t left;
351
352 if (offset > d->size)
353 return -EINVAL;
354
355 if (!d->cached)
356 return 0;
357
358 left = d->size - offset;
359 total_len = left > size ? size : left;
360 memcpy(buf, cache + offset, total_len);
361
362 return total_len;
363 }
364
365 return read_file_fuse(path, buf, size, d);
366 }
367
368 #define INITSCOPE "/init.scope"
369 void prune_init_slice(char *cg)
370 {
371 char *point;
372 size_t cg_len = strlen(cg), initscope_len = strlen(INITSCOPE);
373
374 if (cg_len < initscope_len)
375 return;
376
377 point = cg + cg_len - initscope_len;
378 if (strcmp(point, INITSCOPE) == 0) {
379 if (point == cg)
380 *(point + 1) = '\0';
381 else
382 *point = '\0';
383 }
384 }
385
386 int wait_for_pid(pid_t pid)
387 {
388 int status, ret;
389
390 if (pid <= 0)
391 return -1;
392
393 again:
394 ret = waitpid(pid, &status, 0);
395 if (ret == -1) {
396 if (errno == EINTR)
397 goto again;
398 return -1;
399 }
400 if (ret != pid)
401 goto again;
402 if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
403 return -1;
404 return 0;
405 }
406
407 static ssize_t read_nointr(int fd, void *buf, size_t count)
408 {
409 ssize_t ret;
410 again:
411 ret = read(fd, buf, count);
412 if (ret < 0 && errno == EINTR)
413 goto again;
414
415 return ret;
416 }
417
418 static void *must_realloc(void *orig, size_t sz)
419 {
420 void *ret;
421
422 do {
423 ret = realloc(orig, sz);
424 } while (!ret);
425
426 return ret;
427 }
428
429 static char *fd_to_buf(int fd, size_t *length)
430 {
431 __do_free char *copy = NULL;
432
433 if (!length)
434 return NULL;
435
436 *length = 0;
437 for (;;) {
438 ssize_t bytes_read;
439 char buf[4096];
440 char *old = copy;
441
442 bytes_read = read_nointr(fd, buf, sizeof(buf));
443 if (bytes_read < 0)
444 return NULL;
445
446 if (!bytes_read)
447 break;
448
449 copy = must_realloc(old, (*length + bytes_read) * sizeof(*old));
450 memcpy(copy + *length, buf, bytes_read);
451 *length += bytes_read;
452 }
453
454 return move_ptr(copy);
455 }
456
457 static char *file_to_buf(const char *path, size_t *length)
458 {
459 __do_close int fd = -EBADF;
460
461 if (!length)
462 return NULL;
463
464 fd = open(path, O_RDONLY | O_CLOEXEC);
465 if (fd < 0)
466 return NULL;
467
468 return fd_to_buf(fd, length);
469 }
470
471 FILE *fopen_cached(const char *path, const char *mode, void **caller_freed_buffer)
472 {
473 __do_free char *buf = NULL;
474 size_t len = 0;
475 FILE *f;
476
477 buf = file_to_buf(path, &len);
478 if (!buf)
479 return NULL;
480
481 f = fmemopen(buf, len, mode);
482 if (!f)
483 return NULL;
484 *caller_freed_buffer = move_ptr(buf);
485 return f;
486 }
487
488 FILE *fdopen_cached(int fd, const char *mode, void **caller_freed_buffer)
489 {
490 __do_free char *buf = NULL;
491 size_t len = 0;
492 FILE *f;
493
494 buf = fd_to_buf(fd, &len);
495 if (!buf)
496 return NULL;
497
498 f = fmemopen(buf, len, mode);
499 if (!f)
500 return NULL;
501
502 *caller_freed_buffer = move_ptr(buf);
503 return f;
504 }
505
506 ssize_t write_nointr(int fd, const void *buf, size_t count)
507 {
508 ssize_t ret;
509
510 do {
511 ret = write(fd, buf, count);
512 } while (ret < 0 && errno == EINTR);
513
514 return ret;
515 }
516
517 int safe_uint64(const char *numstr, uint64_t *converted, int base)
518 {
519 char *err = NULL;
520 uint64_t u;
521
522 while (isspace(*numstr))
523 numstr++;
524
525 if (*numstr == '-')
526 return -EINVAL;
527
528 errno = 0;
529 u = strtoull(numstr, &err, base);
530 if (errno == ERANGE && u == UINT64_MAX)
531 return -ERANGE;
532
533 if (err == numstr || *err != '\0')
534 return -EINVAL;
535
536 *converted = u;
537 return 0;
538 }
539
540 static int char_left_gc(const char *buffer, size_t len)
541 {
542 size_t i;
543
544 for (i = 0; i < len; i++) {
545 if (buffer[i] == ' ' ||
546 buffer[i] == '\t')
547 continue;
548
549 return i;
550 }
551
552 return 0;
553 }
554
555 static int char_right_gc(const char *buffer, size_t len)
556 {
557 int i;
558
559 for (i = len - 1; i >= 0; i--) {
560 if (buffer[i] == ' ' ||
561 buffer[i] == '\t' ||
562 buffer[i] == '\n' ||
563 buffer[i] == '\0')
564 continue;
565
566 return i + 1;
567 }
568
569 return 0;
570 }
571
572 char *trim_whitespace_in_place(char *buffer)
573 {
574 buffer += char_left_gc(buffer, strlen(buffer));
575 buffer[char_right_gc(buffer, strlen(buffer))] = '\0';
576 return buffer;
577 }