]> git.proxmox.com Git - mirror_lxc.git/blob - src/lxc/ringbuf.c
Merge pull request #3235 from xinhua9569/master
[mirror_lxc.git] / src / lxc / ringbuf.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #ifndef _GNU_SOURCE
4 #define _GNU_SOURCE 1
5 #endif
6 #define __STDC_FORMAT_MACROS
7 #include <errno.h>
8 #include <inttypes.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <sys/mman.h>
14 #include <unistd.h>
15
16 #include "config.h"
17 #include "ringbuf.h"
18 #include "syscall_wrappers.h"
19 #include "utils.h"
20
21 int lxc_ringbuf_create(struct lxc_ringbuf *buf, size_t size)
22 {
23 char *tmp;
24 int ret;
25 int memfd = -1;
26
27 buf->size = size;
28 buf->r_off = 0;
29 buf->w_off = 0;
30
31 /* verify that we are at least given the multiple of a page size */
32 if (buf->size % lxc_getpagesize())
33 return -EINVAL;
34
35 buf->addr = mmap(NULL, buf->size * 2, PROT_NONE,
36 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
37 if (buf->addr == MAP_FAILED)
38 return -EINVAL;
39
40 memfd = memfd_create(".lxc_ringbuf", MFD_CLOEXEC);
41 if (memfd < 0) {
42 char template[] = P_tmpdir "/.lxc_ringbuf_XXXXXX";
43
44 if (errno != ENOSYS)
45 goto on_error;
46
47 memfd = lxc_make_tmpfile(template, true);
48 }
49 if (memfd < 0)
50 goto on_error;
51
52 ret = ftruncate(memfd, buf->size);
53 if (ret < 0)
54 goto on_error;
55
56 tmp = mmap(buf->addr, buf->size, PROT_READ | PROT_WRITE,
57 MAP_FIXED | MAP_SHARED, memfd, 0);
58 if (tmp == MAP_FAILED || tmp != buf->addr)
59 goto on_error;
60
61 tmp = mmap(buf->addr + buf->size, buf->size, PROT_READ | PROT_WRITE,
62 MAP_FIXED | MAP_SHARED, memfd, 0);
63 if (tmp == MAP_FAILED || tmp != (buf->addr + buf->size))
64 goto on_error;
65
66 close(memfd);
67
68 return 0;
69
70 on_error:
71 lxc_ringbuf_release(buf);
72 if (memfd >= 0)
73 close(memfd);
74 return -1;
75 }
76
77 void lxc_ringbuf_move_read_addr(struct lxc_ringbuf *buf, size_t len)
78 {
79 buf->r_off += len;
80
81 if (buf->r_off < buf->size)
82 return;
83
84 /* wrap around */
85 buf->r_off -= buf->size;
86 buf->w_off -= buf->size;
87 }
88
89 /**
90 * lxc_ringbuf_write - write a message to the ringbuffer
91 * - The size of the message should never be greater than the size of the whole
92 * ringbuffer.
93 * - The write method will always succeed i.e. it will always advance the r_off
94 * if it detects that there's not enough space available to write the
95 * message.
96 */
97 int lxc_ringbuf_write(struct lxc_ringbuf *buf, const char *msg, size_t len)
98 {
99 char *w_addr;
100 uint64_t free;
101
102 /* sanity check: a write should never exceed the ringbuffer's total size */
103 if (len > buf->size)
104 return -EFBIG;
105
106 free = lxc_ringbuf_free(buf);
107
108 /* not enough space left so advance read address */
109 if (len > free)
110 lxc_ringbuf_move_read_addr(buf, len);
111 w_addr = lxc_ringbuf_get_write_addr(buf);
112 memcpy(w_addr, msg, len);
113 lxc_ringbuf_move_write_addr(buf, len);
114 return 0;
115 }
116
117 int lxc_ringbuf_read(struct lxc_ringbuf *buf, char *out, size_t *len)
118 {
119 uint64_t used;
120
121 /* there's nothing to read */
122 if (buf->r_off == buf->w_off)
123 return -ENODATA;
124
125 /* read maximum amount available */
126 used = lxc_ringbuf_used(buf);
127 if (used < *len)
128 *len = used;
129
130 /* copy data to reader but don't advance addr */
131 memcpy(out, lxc_ringbuf_get_read_addr(buf), *len);
132 out[*len - 1] = '\0';
133 return 0;
134 }