The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
pipe.c
Go to the documentation of this file.
1/*
2 * This program is 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: 98512ef941fe5d69d84ebd6209d7ae929ddcdf8f $
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 if (size > 0) {
74 if (eof) {
75 my->cb.eof(&my->bio);
76 my->cb.eof = NULL;
77
78 } else {
79 (void) my->cb.write_resume(&my->bio);
80 }
81
82 } else {
83 (void) my->cb.read_blocked(&my->bio);
84 }
85
86 return size;
87}
88
89
90/** Write to the pipe.
91 *
92 * Once EOF is set, no further writes are possible.
93 */
94static ssize_t fr_bio_pipe_write(fr_bio_t *bio, UNUSED void *packet_ctx, void const *buffer, size_t size)
95{
96 size_t room;
97 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
98
99 pthread_mutex_lock(&my->mutex);
100 room = fr_bio_buf_write_room(&my->buf);
101 if (room > 0) {
102 if (room < size) size = room;
103 (void) fr_bio_buf_write(&my->buf, buffer, size); /* always succeeds */
104 } else {
105 size = 0;
106 }
107 pthread_mutex_unlock(&my->mutex);
108
109 if (size > 0) {
110 (void) my->cb.read_resume(&my->bio);
111 } else {
112 (void) my->cb.write_blocked(&my->bio);
113 }
114
115 return size;
116}
117
118/** Shutdown callback.
119 *
120 */
122{
123 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
124
125 pthread_mutex_lock(&my->mutex);
126 my->bio.read = fr_bio_fail_read;
127 my->bio.write = fr_bio_fail_write;
128 pthread_mutex_unlock(&my->mutex);
129
130 return 0;
131}
132
133static ssize_t fr_bio_pipe_shutdown_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
134{
135 fr_strerror_const("BIO has been closed");
136 return fr_bio_error(SHUTDOWN);
137}
138
139/** Set EOF.
140 *
141 * Either side can set EOF, in which case pending reads are still processed. Writes return EOF immediately.
142 * Readers return pending data, and then EOF.
143 */
144static int fr_bio_pipe_eof(fr_bio_t *bio)
145{
146 int rcode;
147 fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
148
149 /*
150 * @todo - fr_bio_eof() sets our read to NULL read before this callback is run. That has to be
151 * addressed.
152 */
153 pthread_mutex_lock(&my->mutex);
154 my->eof = true;
155 my->bio.write = fr_bio_pipe_shutdown_write;
156 if (fr_bio_buf_used(&my->buf) == 0) {
157 my->bio.read = fr_bio_null_read;
158 rcode = 0;
159 } else {
160 rcode = -1; /* can't close this BIO yet */
161 }
162 pthread_mutex_unlock(&my->mutex);
163
164 return rcode;
165}
166
167/** Allocate a thread-safe pipe which can be used for both reads and writes.
168 *
169 * Due to talloc issues with multiple threads, if the caller wants a bi-directional pipe, this function will
170 * need to be called twice. That way a free in each context won't result in a race condition on two mutex
171 * locks.
172 *
173 * For now, it's too difficult to emulate the pipe[2] behavior, where two identical "connected" things are
174 * returned, and either can be used for reading or for writing.
175 *
176 * i.e. a pipe is really a mutex-protected memory buffer. One side should call write (and never read). The
177 * other side should call read (and never write).
178 *
179 * The pipe should be freed only after both ends have set EOF.
180 */
181fr_bio_t *fr_bio_pipe_alloc(TALLOC_CTX *ctx, fr_bio_cb_funcs_t *cb, size_t buffer_size)
182{
185
186 if (!cb->read_resume || !cb->write_resume) return NULL;
187 if (!cb->read_blocked || !cb->write_blocked) return NULL;
188 if (!cb->eof) return NULL;
189
190 if (buffer_size < 1024) buffer_size = 1024;
191 if (buffer_size > (1 << 20)) buffer_size = (1 << 20);
192
193 my = talloc_zero(ctx, fr_bio_pipe_t);
194 if (!my) return NULL;
195
196 buffer = talloc_array(my, uint8_t, buffer_size);
197 if (!buffer) {
199 return NULL;
200 }
201
202 my->cb = *cb;
203
204 fr_bio_buf_init(&my->buf, buffer, buffer_size);
205
206 pthread_mutex_init(&my->mutex, NULL);
207
208 my->bio.read = fr_bio_pipe_read;
209 my->bio.write = fr_bio_pipe_write;
210 my->cb.shutdown = fr_bio_pipe_shutdown;
211 my->priv_cb.eof = fr_bio_pipe_eof;
212
213 talloc_set_destructor(my, fr_bio_pipe_destructor);
214 return (fr_bio_t *) my;
215}
static int const char char buffer[256]
Definition acutest.h:578
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
#define FR_BIO_DESTRUCTOR_COMMON
Define a common destructor pattern.
Definition bio_priv.h:64
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:317
void fr_bio_shutdown & my
Definition fd_errno.h:70
talloc_free(hp)
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
ssize_t fr_bio_fail_read(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void *buffer, UNUSED size_t size)
Always return error on read.
Definition null.c:47
ssize_t fr_bio_fail_write(UNUSED fr_bio_t *bio, UNUSED void *packet_ctx, UNUSED void const *buffer, UNUSED size_t size)
Always return an error on write.
Definition null.c:56
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:133
static int fr_bio_pipe_eof(fr_bio_t *bio)
Set EOF.
Definition pipe.c:144
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:94
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:121
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:181
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