xref: /openbsd-src/usr.sbin/syslogd/ringbuf.c (revision b9fc9a728fce9c4289b7e9a992665e28d5629a54)
1 /* $OpenBSD: ringbuf.c,v 1.8 2015/01/16 06:40:21 deraadt Exp $ */
2 
3 /*
4  * Copyright (c) 2004 Damien Miller
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the above
8  * copyright notice and this permission notice appear in all copies.
9  *
10  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
11  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
12  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
13  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
14  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
15  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
16  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17  */
18 
19 /*
20  * Simple ringbuffer for lines of text.
21  */
22 
23 #include <sys/types.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "syslogd.h"
29 
30 #define MINIMUM(a, b)	(((a) < (b)) ? (a) : (b))
31 
32 /* Initialise a ring buffer */
33 struct ringbuf *
34 ringbuf_init(size_t len)
35 {
36 	struct ringbuf *ret;
37 
38 	if (len == 0 || (ret = malloc(sizeof(*ret))) == NULL)
39 		return (NULL);
40 
41 	if ((ret->buf = malloc(len)) == NULL) {
42 		free(ret);
43 		return (NULL);
44 	}
45 
46 	ret->len = len;
47 	ret->start = ret->end = 0;
48 
49 	return (ret);
50 }
51 
52 /* Free a ring buffer */
53 void
54 ringbuf_free(struct ringbuf *rb)
55 {
56 	free(rb->buf);
57 	free(rb);
58 }
59 
60 /* Clear a ring buffer */
61 void
62 ringbuf_clear(struct ringbuf *rb)
63 {
64 	rb->start = rb->end = 0;
65 }
66 
67 /* Return the number of bytes used in a ringbuffer */
68 size_t
69 ringbuf_used(struct ringbuf *rb)
70 {
71 	return ((rb->len + rb->end - rb->start) % rb->len);
72 }
73 
74 /*
75  * Append a line to a ring buffer, will delete lines from start
76  * of buffer as necessary
77  */
78 int
79 ringbuf_append_line(struct ringbuf *rb, char *line)
80 {
81 	size_t llen, used, copy_len;
82 	int overflow = 0;
83 
84 	if (rb == NULL || line == NULL)
85 		return (-1);
86 
87 	llen = strlen(line);
88 	if (llen == 0)
89 		return (-1);
90 
91 	if (line[llen - 1] != '\n')
92 		llen++; /* one extra for appended '\n' */
93 
94 	if (llen >= rb->len)
95 		return (-1);
96 
97 	/*
98 	 * If necessary, advance start pointer to make room for appended
99 	 * string. Ensure that start pointer is at the beginning of a line
100 	 * once we are done (i.e move to after '\n').
101 	 */
102 	used = ringbuf_used(rb);
103 	if (used + llen >= rb->len) {
104 		rb->start = (rb->start + used + llen - rb->len) % rb->len;
105 
106 		/* Find next '\n' */
107 		while (rb->buf[rb->start] != '\n')
108 			rb->start = (rb->start + 1) % rb->len;
109 		/* Skip it */
110 		rb->start = (rb->start + 1) % rb->len;
111 
112 		overflow = 1;
113 	}
114 
115 	/*
116 	 * Now append string, starting from last pointer and wrapping if
117 	 * necessary
118 	 */
119 	if (rb->end + llen > rb->len) {
120 		copy_len = rb->len - rb->end;
121 		memcpy(rb->buf + rb->end, line, copy_len);
122 		memcpy(rb->buf, line + copy_len, llen - copy_len - 1);
123 		rb->buf[llen - copy_len - 1] = '\n';
124 	} else {
125 		memcpy(rb->buf + rb->end, line, llen - 1);
126 		rb->buf[rb->end + llen - 1] = '\n';
127 	}
128 
129 	rb->end = (rb->end + llen) % rb->len;
130 
131 	return (overflow);
132 }
133 
134 /*
135  * Copy and nul-terminate a ringbuffer to a string.
136  */
137 ssize_t
138 ringbuf_to_string(char *buf, size_t len, struct ringbuf *rb)
139 {
140 	size_t copy_len, n;
141 
142 	if (buf == NULL || rb == NULL || len == 0)
143 		return (-1);
144 
145 	copy_len = MINIMUM(len - 1, ringbuf_used(rb));
146 
147 	if (copy_len == 0)
148 		return (copy_len);
149 
150 	if (rb->start < rb->end)
151 		memcpy(buf, rb->buf + rb->start, copy_len);
152 	else {
153 		/* If the buffer is wrapped, copy each hunk separately */
154 		n = rb->len - rb->start;
155 		memcpy(buf, rb->buf + rb->start, MINIMUM(n, copy_len));
156 		if (copy_len > n)
157 			memcpy(buf + n, rb->buf, MINIMUM(rb->end, copy_len - n));
158 	}
159 	buf[copy_len] = '\0';
160 
161 	return (ringbuf_used(rb));
162 }
163