]> git.proxmox.com Git - mirror_lxc.git/blame - src/lxc/ringbuf.c
mainloop: capture output of short-lived init procs
[mirror_lxc.git] / src / lxc / ringbuf.c
CommitLineData
f3d05ee6
CB
1/* liblxcapi
2 *
3 * Copyright © 2017 Christian Brauner <christian.brauner@ubuntu.com>.
4 * Copyright © 2017 Canonical Ltd.
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License version 2, as
8 * published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20#define _GNU_SOURCE
21#define __STDC_FORMAT_MACROS
22#include <errno.h>
23#include <inttypes.h>
24#include <stdbool.h>
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <unistd.h>
29#include <sys/mman.h>
30
31#include "ringbuf.h"
32#include "utils.h"
33
34int lxc_ringbuf_create(struct lxc_ringbuf *buf, size_t size)
35{
36 char *tmp;
37 int ret;
38 int memfd = -1;
39
40 buf->size = size;
41 buf->r_off = 0;
42 buf->w_off = 0;
43
44 /* verify that we are at least given the multiple of a page size */
45 if (buf->size % lxc_getpagesize())
46 return -EINVAL;
47
48 buf->addr = mmap(NULL, buf->size * 2, PROT_NONE,
49 MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
50 if (buf->addr == MAP_FAILED)
51 return -EINVAL;
52
53 memfd = memfd_create(".lxc_ringbuf", MFD_CLOEXEC);
54 if (memfd < 0) {
55 if (errno != ENOSYS)
56 goto on_error;
57
58 memfd = lxc_make_tmpfile((char *){P_tmpdir"/.lxc_ringbuf_XXXXXX"}, true);
59 }
60 if (memfd < 0)
61 goto on_error;
62
63 ret = ftruncate(memfd, buf->size);
64 if (ret < 0)
65 goto on_error;
66
67 tmp = mmap(buf->addr, buf->size, PROT_READ | PROT_WRITE,
68 MAP_FIXED | MAP_SHARED, memfd, 0);
69 if (tmp == MAP_FAILED || tmp != buf->addr)
70 goto on_error;
71
72 tmp = mmap(buf->addr + buf->size, buf->size, PROT_READ | PROT_WRITE,
73 MAP_FIXED | MAP_SHARED, memfd, 0);
74 if (tmp == MAP_FAILED || tmp != (buf->addr + buf->size))
75 goto on_error;
76
77 close(memfd);
78
79 return 0;
80
81on_error:
82 lxc_ringbuf_release(buf);
83 if (memfd >= 0)
84 close(memfd);
85 return -1;
86}
87
88void lxc_ringbuf_move_read_addr(struct lxc_ringbuf *buf, size_t len)
89{
90 buf->r_off += len;
91
92 if (buf->r_off < buf->size)
93 return;
94
95 /* wrap around */
96 buf->r_off -= buf->size;
97 buf->w_off -= buf->size;
98}
99
100/**
101 * lxc_ringbuf_write - write a message to the ringbuffer
102 * - The size of the message should never be greater than the size of the whole
103 * ringbuffer.
104 * - The write method will always succeed i.e. it will always advance the r_off
105 * if it detects that there's not enough space available to write the
106 * message.
107 */
108int lxc_ringbuf_write(struct lxc_ringbuf *buf, const char *msg, size_t len)
109{
110 char *w_addr;
111 uint64_t free;
112
113 /* sanity check: a write should never exceed the ringbuffer's total size */
114 if (len > buf->size)
115 return -EFBIG;
116
117 free = lxc_ringbuf_free(buf);
118
119 /* not enough space left so advance read address */
120 if (len > free)
121 lxc_ringbuf_move_read_addr(buf, len);
122 w_addr = lxc_ringbuf_get_write_addr(buf);
123 memcpy(w_addr, msg, len);
124 lxc_ringbuf_move_write_addr(buf, len);
125 return 0;
126}
127
128int lxc_ringbuf_read(struct lxc_ringbuf *buf, char *out, size_t *len)
129{
130 uint64_t used;
131
132 /* there's nothing to read */
133 if (buf->r_off == buf->w_off)
134 return -ENODATA;
135
136 /* read maximum amount available */
137 used = lxc_ringbuf_used(buf);
138 if (used < *len)
139 *len = used;
140
141 /* copy data to reader but don't advance addr */
142 memcpy(out, lxc_ringbuf_get_read_addr(buf), *len);
143 out[*len - 1] = '\0';
144 return 0;
145}