]>
git.proxmox.com Git - mirror_lxcfs.git/blob - src/utils.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
16 #include <sys/epoll.h>
18 #include <sys/types.h>
26 #include "memory_utils.h"
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.
36 char *must_strcat(char **src
, size_t *sz
, size_t *asz
, const char *format
, ...)
38 char tmp
[BUF_RESERVE_SIZE
];
42 va_start (args
, format
);
43 tmplen
= vsnprintf(tmp
, BUF_RESERVE_SIZE
, format
, args
);
46 if (!*src
|| tmplen
+ *sz
+ 1 >= *asz
) {
49 str
= realloc(*src
, *asz
+ BUF_RESERVE_SIZE
);
52 *asz
+= BUF_RESERVE_SIZE
;
54 memcpy((*src
) +*sz
, tmp
, tmplen
+1); /* include the \0 */
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/
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.
71 static int in_same_namespace(pid_t pid1
, pid_t pid2
, const char *ns
)
73 __do_close
int ns_fd1
= -1, ns_fd2
= -1;
75 struct stat ns_st1
, ns_st2
;
77 ns_fd1
= preserve_ns(pid1
, ns
);
79 /* The kernel does not support this namespace. This is not an
88 ns_fd2
= preserve_ns(pid2
, ns
);
92 ret
= fstat(ns_fd1
, &ns_st1
);
96 ret
= fstat(ns_fd2
, &ns_st2
);
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
))
104 /* processes are in different namespaces */
105 return move_fd(ns_fd2
);
108 bool is_shared_pidns(pid_t pid
)
110 __do_close
int fd
= -EBADF
;
115 fd
= in_same_namespace(pid
, getpid(), "pid");
122 int preserve_ns(const int pid
, const char *ns
)
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
];
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
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
) {
141 return open(path
, O_RDONLY
| O_CLOEXEC
);
144 void do_release_file_info(struct fuse_file_info
*fi
)
148 f
= INTTYPE_TO_PTR(fi
->fh
);
154 free_disarm(f
->controller
);
155 free_disarm(f
->cgroup
);
156 free_disarm(f
->file
);
161 #define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
163 bool wait_for_sock(int sock
, int timeout
)
165 __do_close
int epfd
= -EBADF
;
166 struct epoll_event ev
;
167 int ret
, now
, starttime
, deltatime
;
169 if ((starttime
= time(NULL
)) < 0)
172 epfd
= epoll_create(1);
174 return log_error(false, "%m - Failed to create epoll socket");
176 ev
.events
= POLLIN_SET
;
178 if (epoll_ctl(epfd
, EPOLL_CTL_ADD
, sock
, &ev
) < 0)
179 return log_error(false, "Failed adding socket to epoll: %m");
182 if ((now
= time(NULL
)) < 0)
185 deltatime
= (starttime
+ timeout
) - now
;
189 ret
= epoll_wait(epfd
, &ev
, 1, 1000*deltatime
+ 1);
190 if (ret
< 0 && errno
== EINTR
)
199 bool recv_creds(int sock
, struct ucred
*cred
, char *v
)
201 struct msghdr msg
= {};
203 struct cmsghdr
*cmsg
;
205 char cmsgbuf
[CMSG_SPACE(sizeof(*cred
))] = {};
211 msg
.msg_control
= cmsgbuf
;
212 msg
.msg_controllen
= sizeof(cmsgbuf
);
215 iov
.iov_len
= sizeof(buf
);
221 ret
= setsockopt(sock
, SOL_SOCKET
, SO_PASSCRED
, &optval
, sizeof(optval
));
223 return log_error(false, "Failed to set passcred: %s\n", strerror(errno
));
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
));
229 if (!wait_for_sock(sock
, 2))
230 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno
));
232 ret
= recvmsg(sock
, &msg
, MSG_DONTWAIT
);
234 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno
));
236 cmsg
= CMSG_FIRSTHDR(&msg
);
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
));
248 static int msgrecv(int sockfd
, void *buf
, size_t len
)
250 if (!wait_for_sock(sockfd
, 2))
253 return recv(sockfd
, buf
, len
, MSG_DONTWAIT
);
256 int send_creds(int sock
, struct ucred
*cred
, char v
, bool pingfirst
)
258 struct msghdr msg
= { 0 };
260 struct cmsghdr
*cmsg
;
261 char cmsgbuf
[CMSG_SPACE(sizeof(*cred
))];
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
);
269 msg
.msg_control
= cmsgbuf
;
270 msg
.msg_controllen
= sizeof(cmsgbuf
);
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
));
283 iov
.iov_len
= sizeof(buf
);
287 if (sendmsg(sock
, &msg
, 0) < 0) {
289 return log_error(SEND_CREDS_NOTSK
, "%s - Failed at sendmsg: %d", strerror(errno
), SEND_CREDS_NOTSK
);
291 return log_error(SEND_CREDS_FAIL
, "%s - Failed at sendmsg: %d", strerror(errno
), SEND_CREDS_FAIL
);
294 return SEND_CREDS_OK
;
297 int read_file_fuse(const char *path
, char *buf
, size_t size
, struct file_info
*d
)
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
;
305 f
= fopen(path
, "re");
309 while (getline(&line
, &linelen
, f
) != -1) {
312 l
= snprintf(cache
, cache_size
, "%s", line
);
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");
324 if (total_len
> size
)
327 /* read from off 0 */
328 memcpy(buf
, d
->buf
, total_len
);
330 if (d
->size
> (int)total_len
)
331 d
->cached
= d
->size
- total_len
;
336 int read_file_fuse_with_offset(const char *path
, char *buf
, size_t size
,
337 off_t offset
, struct file_info
*d
)
340 ssize_t total_len
= 0;
341 char *cache
= d
->buf
;
344 if (offset
> d
->size
)
350 left
= d
->size
- offset
;
351 total_len
= left
> size
? size
: left
;
352 memcpy(buf
, cache
+ offset
, total_len
);
357 return read_file_fuse(path
, buf
, size
, d
);
360 #define INITSCOPE "/init.scope"
361 void prune_init_slice(char *cg
)
364 size_t cg_len
= strlen(cg
), initscope_len
= strlen(INITSCOPE
);
366 if (cg_len
< initscope_len
)
369 point
= cg
+ cg_len
- initscope_len
;
370 if (strcmp(point
, INITSCOPE
) == 0) {
378 int wait_for_pid(pid_t pid
)
386 ret
= waitpid(pid
, &status
, 0);
394 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
399 static ssize_t
read_nointr(int fd
, void *buf
, size_t count
)
403 ret
= read(fd
, buf
, count
);
404 if (ret
< 0 && errno
== EINTR
)
410 static void *must_realloc(void *orig
, size_t sz
)
415 ret
= realloc(orig
, sz
);
421 static char *fd_to_buf(int fd
, size_t *length
)
423 __do_free
char *copy
= NULL
;
434 bytes_read
= read_nointr(fd
, buf
, sizeof(buf
));
441 copy
= must_realloc(old
, (*length
+ bytes_read
) * sizeof(*old
));
442 memcpy(copy
+ *length
, buf
, bytes_read
);
443 *length
+= bytes_read
;
446 return move_ptr(copy
);
449 static char *file_to_buf(const char *path
, size_t *length
)
451 __do_close
int fd
= -EBADF
;
456 fd
= open(path
, O_RDONLY
| O_CLOEXEC
);
460 return fd_to_buf(fd
, length
);
463 FILE *fopen_cached(const char *path
, const char *mode
, void **caller_freed_buffer
)
465 __do_free
char *buf
= NULL
;
469 buf
= file_to_buf(path
, &len
);
473 f
= fmemopen(buf
, len
, mode
);
476 *caller_freed_buffer
= move_ptr(buf
);
480 FILE *fdopen_cached(int fd
, const char *mode
, void **caller_freed_buffer
)
482 __do_free
char *buf
= NULL
;
486 buf
= fd_to_buf(fd
, &len
);
490 f
= fmemopen(buf
, len
, mode
);
494 *caller_freed_buffer
= move_ptr(buf
);
498 ssize_t
write_nointr(int fd
, const void *buf
, size_t count
)
503 ret
= write(fd
, buf
, count
);
504 } while (ret
< 0 && errno
== EINTR
);
509 int safe_uint64(const char *numstr
, uint64_t *converted
, int base
)
514 while (isspace(*numstr
))
521 u
= strtoull(numstr
, &err
, base
);
522 if (errno
== ERANGE
&& u
== UINT64_MAX
)
525 if (err
== numstr
|| *err
!= '\0')
532 int safe_uint32(const char *numstr
, uint32_t *converted
, int base
)
537 while (isspace(*numstr
))
544 uli
= strtoul(numstr
, &err
, base
);
545 if (errno
== ERANGE
&& uli
== UINT32_MAX
)
548 if (err
== numstr
|| *err
!= '\0')
551 *converted
= (uint32_t)uli
;
555 static int char_left_gc(const char *buffer
, size_t len
)
559 for (i
= 0; i
< len
; i
++) {
560 if (buffer
[i
] == ' ' ||
570 static int char_right_gc(const char *buffer
, size_t len
)
574 for (i
= len
- 1; i
>= 0; i
--) {
575 if (buffer
[i
] == ' ' ||
587 char *trim_whitespace_in_place(char *buffer
)
589 buffer
+= char_left_gc(buffer
, strlen(buffer
));
590 buffer
[char_right_gc(buffer
, strlen(buffer
))] = '\0';
594 #define BATCH_SIZE 50
595 static int batch_realloc(char **mem
, size_t oldlen
, size_t newlen
)
597 int newbatches
= (newlen
/ BATCH_SIZE
) + 1;
598 int oldbatches
= (oldlen
/ BATCH_SIZE
) + 1;
600 if (!*mem
|| newbatches
> oldbatches
) {
603 tmp
= realloc(*mem
, newbatches
* BATCH_SIZE
);
612 static int append_line(char **dest
, size_t oldlen
, char *new, size_t newlen
)
615 size_t full
= oldlen
+ newlen
;
617 ret
= batch_realloc(dest
, oldlen
, full
+ 1);
621 memcpy(*dest
+ oldlen
, new, newlen
+ 1);
625 /* Slurp in a whole file */
626 char *read_file_at(int dfd
, const char *fnam
, unsigned int o_flags
)
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;
634 fd
= openat(dfd
, fnam
, o_flags
, 0);
638 f
= fdopen(fd
, "re");
641 /* Transfer ownership to fdopen(). */
644 while ((linelen
= getline(&line
, &len
, f
)) != -1) {
645 if (append_line(&buf
, fulllen
, line
, linelen
))
650 return move_ptr(buf
);
653 DIR *opendir_flags(const char *path
, int flags
)
655 __do_close
int dfd
= -EBADF
;
658 dfd
= open(path
, O_DIRECTORY
| flags
);
662 dirp
= fdopendir(dfd
);
664 move_fd(dfd
); /* Transfer ownership to fdopendir(). */
669 int get_task_personality(pid_t pid
, __u32
*personality
)
671 __do_close
int fd
= -EBADF
;
673 char path
[STRLITERALLEN("/proc//personality") + INTTYPE_TO_STRLEN(pid_t
) + 1];
674 /* seq_printf(m, "%08x\n", task->personality); */
677 ret
= strnprintf(path
, sizeof(path
), "/proc/%d/personality", pid
);
681 fd
= open(path
, O_RDONLY
| O_CLOEXEC
);
685 ret
= read_nointr(fd
, buf
, sizeof(buf
) - 1);
688 if (safe_uint32(buf
, personality
, 16) < 0)
689 return log_error(-1, "Failed to convert personality %s", buf
);