]> git.proxmox.com Git - mirror_lxcfs.git/blob - src/utils.c
sysfs: correct file size of /sys/devices/system/cpu/online.
[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 char *must_strcat(char **src, size_t *sz, size_t *asz, const char *format, ...)
37 {
38 char tmp[BUF_RESERVE_SIZE];
39 va_list args;
40 int tmplen;
41
42 va_start (args, format);
43 tmplen = vsnprintf(tmp, BUF_RESERVE_SIZE, format, args);
44 va_end(args);
45
46 if (!*src || tmplen + *sz + 1 >= *asz) {
47 char *str;
48 do {
49 str = realloc(*src, *asz + BUF_RESERVE_SIZE);
50 } while (!str);
51 *src = str;
52 *asz += BUF_RESERVE_SIZE;
53 }
54 memcpy((*src) +*sz , tmp, tmplen+1); /* include the \0 */
55 *sz += tmplen;
56
57 return *src;
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 */
71 static int in_same_namespace(pid_t pid1, pid_t pid2, const char *ns)
72 {
73 __do_close int ns_fd1 = -1, ns_fd2 = -1;
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
108 bool is_shared_pidns(pid_t pid)
109 {
110 __do_close int fd = -EBADF;
111
112 if (pid != 1)
113 return false;
114
115 fd = in_same_namespace(pid, getpid(), "pid");
116 if (fd == EINVAL)
117 return true;
118
119 return false;
120 }
121
122 int 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 }
143
144 void do_release_file_info(struct fuse_file_info *fi)
145 {
146 struct file_info *f;
147
148 f = INTTYPE_TO_PTR(fi->fh);
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 }
160
161 #define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
162
163 bool wait_for_sock(int sock, int timeout)
164 {
165 __do_close int epfd = -EBADF;
166 struct epoll_event ev;
167 int ret, now, starttime, deltatime;
168
169 if ((starttime = time(NULL)) < 0)
170 return false;
171
172 epfd = epoll_create(1);
173 if (epfd < 0)
174 return log_error(false, "%m - Failed to create epoll socket");
175
176 ev.events = POLLIN_SET;
177 ev.data.fd = sock;
178 if (epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ev) < 0)
179 return log_error(false, "Failed adding socket to epoll: %m");
180
181 again:
182 if ((now = time(NULL)) < 0)
183 return false;
184
185 deltatime = (starttime + timeout) - now;
186 if (deltatime < 0)
187 return false;
188
189 ret = epoll_wait(epfd, &ev, 1, 1000*deltatime + 1);
190 if (ret < 0 && errno == EINTR)
191 goto again;
192
193 if (ret <= 0)
194 return false;
195
196 return true;
197 }
198
199 bool recv_creds(int sock, struct ucred *cred, char *v)
200 {
201 struct msghdr msg = {};
202 struct iovec iov;
203 struct cmsghdr *cmsg;
204 ssize_t ret;
205 char cmsgbuf[CMSG_SPACE(sizeof(*cred))] = {};
206 char buf = '1';
207 int optval = 1;
208
209 msg.msg_name = NULL;
210 msg.msg_namelen = 0;
211 msg.msg_control = cmsgbuf;
212 msg.msg_controllen = sizeof(cmsgbuf);
213
214 iov.iov_base = &buf;
215 iov.iov_len = sizeof(buf);
216 msg.msg_iov = &iov;
217 msg.msg_iovlen = 1;
218
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
229 if (!wait_for_sock(sock, 2))
230 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno));
231
232 ret = recvmsg(sock, &msg, MSG_DONTWAIT);
233 if (ret < 0)
234 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno));
235
236 cmsg = CMSG_FIRSTHDR(&msg);
237
238 if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(*cred)) &&
239 cmsg->cmsg_level == SOL_SOCKET &&
240 cmsg->cmsg_type == SCM_CREDENTIALS) {
241 memcpy(cred, CMSG_DATA(cmsg), sizeof(*cred));
242 }
243 *v = buf;
244
245 return true;
246 }
247
248 static int msgrecv(int sockfd, void *buf, size_t len)
249 {
250 if (!wait_for_sock(sockfd, 2))
251 return -1;
252
253 return recv(sockfd, buf, len, MSG_DONTWAIT);
254 }
255
256 int 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
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);
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) {
288 if (errno == 3)
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);
292 }
293
294 return SEND_CREDS_OK;
295 }
296
297 int 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
305 f = fopen(path, "re");
306 if (!f)
307 return 0;
308
309 while (getline(&line, &linelen, f) != -1) {
310 ssize_t l;
311
312 l = snprintf(cache, cache_size, "%s", line);
313 if (l < 0)
314 return log_error(0, "Failed to write cache");
315 if ((size_t)l >= cache_size)
316 return log_error(0, "Write to cache was truncated");
317
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
330 if (d->size > (int)total_len)
331 d->cached = d->size - total_len;
332
333 return total_len;
334 }
335
336 int 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;
342 size_t left;
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
360 #define INITSCOPE "/init.scope"
361 void 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)
372 *(point + 1) = '\0';
373 else
374 *point = '\0';
375 }
376 }
377
378 int wait_for_pid(pid_t pid)
379 {
380 int status, ret;
381
382 if (pid <= 0)
383 return -1;
384
385 again:
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 }
398
399 static ssize_t read_nointr(int fd, void *buf, size_t count)
400 {
401 ssize_t ret;
402 again:
403 ret = read(fd, buf, count);
404 if (ret < 0 && errno == EINTR)
405 goto again;
406
407 return ret;
408 }
409
410 static 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
421 static char *fd_to_buf(int fd, size_t *length)
422 {
423 __do_free char *copy = NULL;
424
425 if (!length)
426 return NULL;
427
428 *length = 0;
429 for (;;) {
430 ssize_t bytes_read;
431 char buf[4096];
432 char *old = copy;
433
434 bytes_read = read_nointr(fd, buf, sizeof(buf));
435 if (bytes_read < 0)
436 return NULL;
437
438 if (!bytes_read)
439 break;
440
441 copy = must_realloc(old, (*length + bytes_read) * sizeof(*old));
442 memcpy(copy + *length, buf, bytes_read);
443 *length += bytes_read;
444 }
445
446 return move_ptr(copy);
447 }
448
449 static char *file_to_buf(const char *path, size_t *length)
450 {
451 __do_close int fd = -EBADF;
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
463 FILE *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 }
479
480 FILE *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 }
497
498 ssize_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 }
508
509 int 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 }
531
532 static int char_left_gc(const char *buffer, size_t len)
533 {
534 size_t i;
535
536 for (i = 0; i < len; i++) {
537 if (buffer[i] == ' ' ||
538 buffer[i] == '\t')
539 continue;
540
541 return i;
542 }
543
544 return 0;
545 }
546
547 static int char_right_gc(const char *buffer, size_t len)
548 {
549 int i;
550
551 for (i = len - 1; i >= 0; i--) {
552 if (buffer[i] == ' ' ||
553 buffer[i] == '\t' ||
554 buffer[i] == '\n' ||
555 buffer[i] == '\0')
556 continue;
557
558 return i + 1;
559 }
560
561 return 0;
562 }
563
564 char *trim_whitespace_in_place(char *buffer)
565 {
566 buffer += char_left_gc(buffer, strlen(buffer));
567 buffer[char_right_gc(buffer, strlen(buffer))] = '\0';
568 return buffer;
569 }
570
571 #define BATCH_SIZE 50
572 static int batch_realloc(char **mem, size_t oldlen, size_t newlen)
573 {
574 int newbatches = (newlen / BATCH_SIZE) + 1;
575 int oldbatches = (oldlen / BATCH_SIZE) + 1;
576
577 if (!*mem || newbatches > oldbatches) {
578 char *tmp;
579
580 tmp = realloc(*mem, newbatches * BATCH_SIZE);
581 if (!tmp)
582 return -ENOMEM;
583 *mem = tmp;
584 }
585
586 return 0;
587 }
588
589 static int append_line(char **dest, size_t oldlen, char *new, size_t newlen)
590 {
591 int ret;
592 size_t full = oldlen + newlen;
593
594 ret = batch_realloc(dest, oldlen, full + 1);
595 if (ret)
596 return ret;
597
598 memcpy(*dest + oldlen, new, newlen + 1);
599 return 0;
600 }
601
602 /* Slurp in a whole file */
603 char *read_file_at(int dfd, const char *fnam, unsigned int o_flags)
604 {
605 __do_close int fd = -EBADF;
606 __do_free char *buf = NULL, *line = NULL;
607 __do_fclose FILE *f = NULL;
608 size_t len = 0, fulllen = 0;
609 int linelen;
610
611 fd = openat(dfd, fnam, o_flags, 0);
612 if (fd < 0)
613 return NULL;
614
615 f = fdopen(fd, "re");
616 if (!f)
617 return NULL;
618 /* Transfer ownership to fdopen(). */
619 move_fd(fd);
620
621 while ((linelen = getline(&line, &len, f)) != -1) {
622 if (append_line(&buf, fulllen, line, linelen))
623 return NULL;
624 fulllen += linelen;
625 }
626
627 return move_ptr(buf);
628 }
629
630 DIR *opathdir(const char *path)
631 {
632 __do_close int dfd = -EBADF;
633 DIR *dirp;
634
635 dfd = open(path, O_DIRECTORY | O_PATH | O_CLOEXEC | O_NOFOLLOW);
636 if (dfd < 0)
637 return NULL;
638
639 dirp = fdopendir(dfd);
640 if (dirp)
641 move_fd(dfd); /* Transfer ownership to fdopendir(). */
642
643 return dirp;
644 }