The FreeRADIUS server $Id: f3670dba8951ca10eb4948feb3dc3db9423a334f $
Loading...
Searching...
No Matches
pipe.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or (at
5 * your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: 866b58413c8bea7c60889b07e474542a85b47c22 $
19 * @file lib/bio/pipe.c
20 * @brief BIO abstractions for in-memory pipes
21 *
22 * @copyright 2024 Network RADIUS SAS (legal@networkradius.com)
23 */
24#include <freeradius-devel/bio/bio_priv.h>
25#include <freeradius-devel/bio/null.h>
26#include <freeradius-devel/bio/buf.h>
27
28#include <freeradius-devel/bio/pipe.h>
29
30#include <pthread.h>
31
32/** The pipe bio
33 *
34 */
35typedef struct {
37
38 fr_bio_buf_t buf; //!< for reading and writing
39
40 bool eof; //!< are we at EOF?
41
44
45
47{
49
50 pthread_mutex_destroy(&my->mutex);
51
52 return 0;
53}
54
55/** Read from the pipe.
56 *
57 * Once EOF is set, any pending data is read, and then EOF is returned.
58 */
59static ssize_t fr_bio_pipe_read(fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size)
60{
61 bool eof = false;
62 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
63
64 pthread_mutex_lock(&my->mutex);
65 size = fr_bio_buf_read(&my->buf, buffer, size);
66
67 if (my->eof && (fr_bio_buf_used(&my->buf) == 0)) {
68 eof = true;
69 my->bio.read = fr_bio_null_read;
70 }
71 pthread_mutex_unlock(&my->mutex);
72
73 /*
74 * IF we're at EOF, then don't read anything and there's nothing to wait for.
75 */
76 if (eof) {
77 my->priv_cb.eof = NULL;
78 (void) fr_bio_eof(&my->bio);
79
80 } else if (size > 0) {
81 (void) my->cb.write_resume(&my->bio);
82
83 } else {
84 (void) my->cb.read_blocked(&my->bio);
85 }
86
87 return size;
88}
89
90
91/** Write to the pipe.
92 *
93 * Once EOF is set, no further writes are possible.
94 */
95static ssize_t fr_bio_pipe_write(fr_bio_t *bio, UNUSED void *packet_ctx, void const *buffer, size_t size)
96{
97 size_t room;
98 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
99
100 pthread_mutex_lock(&my->mutex);
101 room = fr_bio_buf_write_room(&my->buf);
102 if (room > 0) {
103 if (room < size) size = room;
104 (void) fr_bio_buf_write(&my->buf, buffer, size); /* always succeeds */
105 } else {
106 size = 0;
107 }
108 pthread_mutex_unlock(&my->mutex);
109
110 if (size > 0) {
111 (void) my->cb.read_resume(&my->bio);
112 } else {
113 (void) my->cb.write_blocked(&my->bio);
114 }
115
116 return size;
117}
118
119/** Shutdown callback.
120 *
121 * We just set the EOF flag, which means that the other end stops using it. fr_bio_shutdown() mangles the
122 * read and write callbacks, so we don't need to do that here.
123 */
125{
126 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
127
128 pthread_mutex_lock(&my->mutex);
129 my->eof = true;
130 pthread_mutex_unlock(&my->mutex);
131
132 return 0;
133}
134
136{
137 fr_strerror_const("BIO has been closed");
138 return fr_bio_error(SHUTDOWN);
139}
140
141/** Set EOF.
142 *
143 * Either side can set EOF, in which case pending reads are still processed. Writes return EOF immediately.
144 * Readers return pending data, and then EOF.
145 */
146static int fr_bio_pipe_eof(fr_bio_t *bio)
147{
148 int rcode;
149 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
150
151 /*
152 * The return value is what fr_bio_eof() uses to decide whether to keep walking back up the
153 * chain, so it has to match fr_bio_mem_eof(): 1 for "nothing here, keep going", and 0 for
154 * "the application still has data to collect from me, stop".
155 *
156 * fr_bio_eof() only sets the read routine of the bio it was given, which is the bio which
157 * hit EOF, so ours is left alone and fr_bio_pipe_read() keeps draining the buffer.
158 */
159 pthread_mutex_lock(&my->mutex);
160 my->eof = true;
161 my->bio.write = fr_bio_pipe_shutdown_write;
162 if (fr_bio_buf_used(&my->buf) == 0) {
163 my->bio.read = fr_bio_null_read;
164 rcode = 1;
165 } else {
166 rcode = 0; /* can't close this BIO yet */
167 }
168 pthread_mutex_unlock(&my->mutex);
169
170 return rcode;
171}
172
173/** Allocate a thread-safe pipe which can be used for both reads and writes.
174 *
175 * Due to talloc issues with multiple threads, if the caller wants a bi-directional pipe, this function will
176 * need to be called twice. That way a free in each context won't result in a race condition on two mutex
177 * locks.
178 *
179 * For now, it's too difficult to emulate the pipe[2] behavior, where two identical "connected" things are
180 * returned, and either can be used for reading or for writing.
181 *
182 * i.e. a pipe is really a mutex-protected memory buffer. One side should call write (and never read). The
183 * other side should call read (and never write).
184 *
185 * The pipe should be freed only after both ends have set EOF.
186 */
187fr_bio_t *fr_bio_pipe_alloc(TALLOC_CTX *ctx, fr_bio_cb_funcs_t *cb, size_t buffer_size)
188{
189 fr_bio_pipe_t *my;
191
192 if (!cb->read_resume || !cb->write_resume) return NULL;
193 if (!cb->read_blocked || !cb->write_blocked) return NULL;
194 if (!cb->eof) return NULL;
195
196 if (buffer_size < 1024) buffer_size = 1024;
197 if (buffer_size > (1 << 20)) buffer_size = (1 << 20);
198
199 my = talloc_zero(ctx, fr_bio_pipe_t);
200 if (!my) return NULL;
201
202 buffer = talloc_array(my, uint8_t, buffer_size);
203 if (!buffer) {
204 talloc_free(my);
205 return NULL;
206 }
207
208 my->cb = *cb;
209
210 fr_bio_buf_init(&my->buf, buffer, buffer_size);
211
212 pthread_mutex_init(&my->mutex, NULL);
213
214 my->bio.read = fr_bio_pipe_read;
215 my->bio.write = fr_bio_pipe_write;
216
217 /*
218 * We need a pipe-specific shutdown, which is separate from the application callback.
219 */
220 my->priv_cb.shutdown = fr_bio_pipe_shutdown;
221 my->priv_cb.eof = fr_bio_pipe_eof;
222
223 talloc_set_destructor(my, fr_bio_pipe_destructor);
224 return (fr_bio_t *) my;
225}
static int const char char buffer[256]
Definition acutest.h:635
fr_bio_callback_t eof
called when the BIO is at EOF
Definition base.h:91
fr_bio_io_t read_resume
"unblocked" is too similar to "blocked"
Definition base.h:97
fr_bio_io_t read_blocked
Definition base.h:94
fr_bio_io_t write_blocked
returns 0 for "couldn't block", 1 for "did block".
Definition base.h:95
#define fr_bio_error(_x)
Definition base.h:200
fr_bio_io_t write_resume
Definition base.h:98
static int packet_ctx
the tests pass only the address of packet_ctx
#define FR_BIO_DESTRUCTOR_COMMON
Define a common destructor pattern.
Definition bio_priv.h:65
size_t fr_bio_buf_read(fr_bio_buf_t *bio_buf, void *buffer, size_t size)
Definition buf.c:48
ssize_t fr_bio_buf_write(fr_bio_buf_t *bio_buf, const void *buffer, size_t size)
Definition buf.c:84
static void fr_bio_buf_init(fr_bio_buf_t *bio_buf, uint8_t *buffer, size_t size)
Definition buf.h:37
static size_t fr_bio_buf_write_room(fr_bio_buf_t const *bio_buf)
Definition buf.h:82
static size_t fr_bio_buf_used(fr_bio_buf_t const *bio_buf)
Definition buf.h:73
#define UNUSED
Definition build.h:384
talloc_free(hp)
fr_bio_t * fr_bio_eof(fr_bio_t *bio)
Internal BIO function to run EOF callbacks.
Definition base.c:279
long int ssize_t
unsigned char uint8_t
ssize_t fr_bio_null_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
Always return 0 on read.
Definition null.c:31
static ssize_t fr_bio_pipe_shutdown_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
Definition pipe.c:135
static int fr_bio_pipe_eof(fr_bio_t *bio)
Set EOF.
Definition pipe.c:146
static ssize_t fr_bio_pipe_write(fr_bio_t *bio, UNUSED void *packet_ctx, void const *buffer, size_t size)
Write to the pipe.
Definition pipe.c:95
bool eof
are we at EOF?
Definition pipe.c:40
static int fr_bio_pipe_shutdown(fr_bio_t *bio)
Shutdown callback.
Definition pipe.c:124
fr_bio_buf_t buf
for reading and writing
Definition pipe.c:38
pthread_mutex_t mutex
Definition pipe.c:42
fr_bio_t * fr_bio_pipe_alloc(TALLOC_CTX *ctx, fr_bio_cb_funcs_t *cb, size_t buffer_size)
Allocate a thread-safe pipe which can be used for both reads and writes.
Definition pipe.c:187
static ssize_t fr_bio_pipe_read(fr_bio_t *bio, UNUSED void *packet_ctx, void *buffer, size_t size)
Read from the pipe.
Definition pipe.c:59
static int fr_bio_pipe_destructor(fr_bio_pipe_t *my)
Definition pipe.c:46
The pipe bio.
Definition pipe.c:35
#define fr_strerror_const(_msg)
Definition strerror.h:223