]>
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.
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.
44 char *must_strcat(char **src
, size_t *sz
, size_t *asz
, const char *format
, ...)
46 char tmp
[BUF_RESERVE_SIZE
];
50 va_start (args
, format
);
51 tmplen
= vsnprintf(tmp
, BUF_RESERVE_SIZE
, format
, args
);
54 if (!*src
|| tmplen
+ *sz
+ 1 >= *asz
) {
57 str
= realloc(*src
, *asz
+ BUF_RESERVE_SIZE
);
60 *asz
+= BUF_RESERVE_SIZE
;
62 memcpy((*src
) +*sz
, tmp
, tmplen
+1); /* include the \0 */
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/
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.
79 static int in_same_namespace(pid_t pid1
, pid_t pid2
, const char *ns
)
81 __do_close
int ns_fd1
= -1, ns_fd2
= -1;
83 struct stat ns_st1
, ns_st2
;
85 ns_fd1
= preserve_ns(pid1
, ns
);
87 /* The kernel does not support this namespace. This is not an
96 ns_fd2
= preserve_ns(pid2
, ns
);
100 ret
= fstat(ns_fd1
, &ns_st1
);
104 ret
= fstat(ns_fd2
, &ns_st2
);
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
))
112 /* processes are in different namespaces */
113 return move_fd(ns_fd2
);
116 bool is_shared_pidns(pid_t pid
)
118 __do_close
int fd
= -EBADF
;
123 fd
= in_same_namespace(pid
, getpid(), "pid");
130 int preserve_ns(const int pid
, const char *ns
)
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
];
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
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
) {
149 return open(path
, O_RDONLY
| O_CLOEXEC
);
152 void do_release_file_info(struct fuse_file_info
*fi
)
156 f
= INTTYPE_TO_PTR(fi
->fh
);
162 free_disarm(f
->controller
);
163 free_disarm(f
->cgroup
);
164 free_disarm(f
->file
);
169 #define POLLIN_SET ( EPOLLIN | EPOLLHUP | EPOLLRDHUP )
171 bool wait_for_sock(int sock
, int timeout
)
173 __do_close
int epfd
= -EBADF
;
174 struct epoll_event ev
;
175 int ret
, now
, starttime
, deltatime
;
177 if ((starttime
= time(NULL
)) < 0)
180 epfd
= epoll_create(1);
182 return log_error(false, "%m - Failed to create epoll socket");
184 ev
.events
= POLLIN_SET
;
186 if (epoll_ctl(epfd
, EPOLL_CTL_ADD
, sock
, &ev
) < 0)
187 return log_error(false, "Failed adding socket to epoll: %m");
190 if ((now
= time(NULL
)) < 0)
193 deltatime
= (starttime
+ timeout
) - now
;
197 ret
= epoll_wait(epfd
, &ev
, 1, 1000*deltatime
+ 1);
198 if (ret
< 0 && errno
== EINTR
)
207 bool recv_creds(int sock
, struct ucred
*cred
, char *v
)
209 struct msghdr msg
= {};
211 struct cmsghdr
*cmsg
;
213 char cmsgbuf
[CMSG_SPACE(sizeof(*cred
))] = {};
219 msg
.msg_control
= cmsgbuf
;
220 msg
.msg_controllen
= sizeof(cmsgbuf
);
223 iov
.iov_len
= sizeof(buf
);
229 ret
= setsockopt(sock
, SOL_SOCKET
, SO_PASSCRED
, &optval
, sizeof(optval
));
231 return log_error(false, "Failed to set passcred: %s\n", strerror(errno
));
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
));
237 if (!wait_for_sock(sock
, 2))
238 return log_error(false, "Timed out waiting for scm_cred: %s\n", strerror(errno
));
240 ret
= recvmsg(sock
, &msg
, MSG_DONTWAIT
);
242 return log_error(false, "Failed to receive scm_cred: %s\n", strerror(errno
));
244 cmsg
= CMSG_FIRSTHDR(&msg
);
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
));
256 static int msgrecv(int sockfd
, void *buf
, size_t len
)
258 if (!wait_for_sock(sockfd
, 2))
261 return recv(sockfd
, buf
, len
, MSG_DONTWAIT
);
264 int send_creds(int sock
, struct ucred
*cred
, char v
, bool pingfirst
)
266 struct msghdr msg
= { 0 };
268 struct cmsghdr
*cmsg
;
269 char cmsgbuf
[CMSG_SPACE(sizeof(*cred
))];
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
);
277 msg
.msg_control
= cmsgbuf
;
278 msg
.msg_controllen
= sizeof(cmsgbuf
);
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
));
291 iov
.iov_len
= sizeof(buf
);
295 if (sendmsg(sock
, &msg
, 0) < 0) {
297 return log_error(SEND_CREDS_NOTSK
, "%s - Failed at sendmsg: %d", strerror(errno
), SEND_CREDS_NOTSK
);
299 return log_error(SEND_CREDS_FAIL
, "%s - Failed at sendmsg: %d", strerror(errno
), SEND_CREDS_FAIL
);
302 return SEND_CREDS_OK
;
305 int read_file_fuse(const char *path
, char *buf
, size_t size
, struct file_info
*d
)
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
;
313 f
= fopen(path
, "re");
317 while (getline(&line
, &linelen
, f
) != -1) {
320 l
= snprintf(cache
, cache_size
, "%s", line
);
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");
332 if (total_len
> size
)
335 /* read from off 0 */
336 memcpy(buf
, d
->buf
, total_len
);
338 if (d
->size
> (int)total_len
)
339 d
->cached
= d
->size
- total_len
;
344 int read_file_fuse_with_offset(const char *path
, char *buf
, size_t size
,
345 off_t offset
, struct file_info
*d
)
348 ssize_t total_len
= 0;
349 char *cache
= d
->buf
;
352 if (offset
> d
->size
)
358 left
= d
->size
- offset
;
359 total_len
= left
> size
? size
: left
;
360 memcpy(buf
, cache
+ offset
, total_len
);
365 return read_file_fuse(path
, buf
, size
, d
);
368 #define INITSCOPE "/init.scope"
369 void prune_init_slice(char *cg
)
372 size_t cg_len
= strlen(cg
), initscope_len
= strlen(INITSCOPE
);
374 if (cg_len
< initscope_len
)
377 point
= cg
+ cg_len
- initscope_len
;
378 if (strcmp(point
, INITSCOPE
) == 0) {
386 int wait_for_pid(pid_t pid
)
394 ret
= waitpid(pid
, &status
, 0);
402 if (!WIFEXITED(status
) || WEXITSTATUS(status
) != 0)
407 static ssize_t
read_nointr(int fd
, void *buf
, size_t count
)
411 ret
= read(fd
, buf
, count
);
412 if (ret
< 0 && errno
== EINTR
)
418 static void *must_realloc(void *orig
, size_t sz
)
423 ret
= realloc(orig
, sz
);
429 static char *fd_to_buf(int fd
, size_t *length
)
431 __do_free
char *copy
= NULL
;
442 bytes_read
= read_nointr(fd
, buf
, sizeof(buf
));
449 copy
= must_realloc(old
, (*length
+ bytes_read
) * sizeof(*old
));
450 memcpy(copy
+ *length
, buf
, bytes_read
);
451 *length
+= bytes_read
;
454 return move_ptr(copy
);
457 static char *file_to_buf(const char *path
, size_t *length
)
459 __do_close
int fd
= -EBADF
;
464 fd
= open(path
, O_RDONLY
| O_CLOEXEC
);
468 return fd_to_buf(fd
, length
);
471 FILE *fopen_cached(const char *path
, const char *mode
, void **caller_freed_buffer
)
473 __do_free
char *buf
= NULL
;
477 buf
= file_to_buf(path
, &len
);
481 f
= fmemopen(buf
, len
, mode
);
484 *caller_freed_buffer
= move_ptr(buf
);
488 FILE *fdopen_cached(int fd
, const char *mode
, void **caller_freed_buffer
)
490 __do_free
char *buf
= NULL
;
494 buf
= fd_to_buf(fd
, &len
);
498 f
= fmemopen(buf
, len
, mode
);
502 *caller_freed_buffer
= move_ptr(buf
);
506 ssize_t
write_nointr(int fd
, const void *buf
, size_t count
)
511 ret
= write(fd
, buf
, count
);
512 } while (ret
< 0 && errno
== EINTR
);
517 int safe_uint64(const char *numstr
, uint64_t *converted
, int base
)
522 while (isspace(*numstr
))
529 u
= strtoull(numstr
, &err
, base
);
530 if (errno
== ERANGE
&& u
== UINT64_MAX
)
533 if (err
== numstr
|| *err
!= '\0')
540 static int char_left_gc(const char *buffer
, size_t len
)
544 for (i
= 0; i
< len
; i
++) {
545 if (buffer
[i
] == ' ' ||
555 static int char_right_gc(const char *buffer
, size_t len
)
559 for (i
= len
- 1; i
>= 0; i
--) {
560 if (buffer
[i
] == ' ' ||
572 char *trim_whitespace_in_place(char *buffer
)
574 buffer
+= char_left_gc(buffer
, strlen(buffer
));
575 buffer
[char_right_gc(buffer
, strlen(buffer
))] = '\0';