The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: a4cf06eec50227ddf42a428f14c9332cb4ed6b37 $
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/mem.h>
27 
28 #include <freeradius-devel/bio/pipe.h>
29 
30 #include <pthread.h>
31 
32 /** The pipe bio
33  *
34  */
35 typedef struct {
37 
39 
40  bool eof; //!< are we at EOF?
41 
42  fr_bio_pipe_cb_funcs_t signal; //!< inform us that the pipe is readable
43 
46 
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  */
59 static ssize_t fr_bio_pipe_read(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
60 {
61  ssize_t rcode;
62  fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
63 
64  fr_assert(my->next != NULL);
65 
66  pthread_mutex_lock(&my->mutex);
67  rcode = my->next->read(my->next, packet_ctx, buffer, size);
68  if ((rcode == 0) && my->eof) {
69  pthread_mutex_unlock(&my->mutex);
70 
71  /*
72  * Don't call our EOF function. But do tell the other BIOs that we're at EOF.
73  */
74  my->priv_cb.eof = NULL;
75  fr_bio_eof(bio);
76  return 0;
77 
78  } else if (rcode > 0) {
79  /*
80  * There is room to write more data.
81  *
82  * @todo - only signal when we transition from BLOCKED to unblocked.
83  */
84  my->signal.writeable(&my->bio);
85  }
86  pthread_mutex_unlock(&my->mutex);
87 
88  return rcode;
89 }
90 
91 
92 /** Write to the pipe.
93  *
94  * Once EOF is set, no further writes are possible.
95  */
96 static ssize_t fr_bio_pipe_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
97 {
98  ssize_t rcode;
99  fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
100 
101  fr_assert(my->next != NULL);
102 
103  pthread_mutex_lock(&my->mutex);
104  if (!my->eof) {
105  rcode = my->next->write(my->next, packet_ctx, buffer, size);
106 
107  /*
108  * There is more data to read.
109  *
110  * @todo - only signal when we transition from no data to data.
111  */
112  if (rcode > 0) {
113  my->signal.readable(&my->bio);
114  }
115 
116  } else {
117  rcode = 0;
118  }
119  pthread_mutex_unlock(&my->mutex);
120 
121  return rcode;
122 }
123 
124 /** Shutdown callback.
125  *
126  */
128 {
129  fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
130 
131  fr_assert(my->next != NULL);
132 
133  pthread_mutex_lock(&my->mutex);
134  fr_bio_shutdown(my->next);
135  pthread_mutex_unlock(&my->mutex);
136 }
137 
138 /** Set EOF.
139  *
140  * Either side can set EOF, in which case pending reads are still processed. Writes return EOF immediately.
141  * Readers return pending data, and then EOF.
142  */
143 static int fr_bio_pipe_eof(fr_bio_t *bio)
144 {
145  fr_bio_pipe_t *my = talloc_get_type_abort(bio, fr_bio_pipe_t);
146 
147  pthread_mutex_lock(&my->mutex);
148  my->eof = true;
149  pthread_mutex_unlock(&my->mutex);
150 
151  /*
152  * We don't know if the other end is at EOF, we have to do a read. So we tell fr_bio_eof() to
153  * stop processing.
154  */
155  return 0;
156 }
157 
158 /** Allocate a thread-safe pipe which can be used for both reads and writes.
159  *
160  * Due to talloc issues with multiple threads, if the caller wants a bi-directional pipe, this function will
161  * need to be called twice. That way a free in each context won't result in a race condition on two mutex
162  * locks.
163  *
164  * For now, iqt's too difficult to emulate the pipe[2] behavior, where two identical "connected" things are
165  * returned, and either can be used for reading or for writing.
166  *
167  * i.e. a pipe is really a mutex-protected memory buffer. One side should call write (and never read). The
168  * other side should call read (and never write).
169  *
170  * The pipe should be freed only after both ends have set EOF.
171  */
172 fr_bio_t *fr_bio_pipe_alloc(TALLOC_CTX *ctx, fr_bio_pipe_cb_funcs_t *cb, size_t buffer_size)
173 {
174  fr_bio_pipe_t *my;
175 
176  if (!cb->readable || !cb->writeable) return NULL;
177 
178  if (buffer_size < 1024) buffer_size = 1024;
179  if (buffer_size > (1 << 20)) buffer_size = (1 << 20);
180 
181  my = talloc_zero(ctx, fr_bio_pipe_t);
182  if (!my) return NULL;
183 
184  my->next = fr_bio_mem_sink_alloc(my, buffer_size);
185  if (!my->next) {
186  talloc_free(my);
187  return NULL;
188  }
189 
190  my->signal = *cb;
191 
192  pthread_mutex_init(&my->mutex, NULL);
193 
194  my->bio.read = fr_bio_pipe_read;
195  my->bio.write = fr_bio_pipe_write;
196  my->cb.shutdown = fr_bio_pipe_shutdown;
197  my->priv_cb.eof = fr_bio_pipe_eof;
198 
199  talloc_set_destructor(my, fr_bio_pipe_destructor);
200  return (fr_bio_t *) my;
201 }
static int const char char buffer[256]
Definition: acutest.h:574
Definition: base.h:112
fr_bio_shutdown & my
Definition: fd_errno.h:59
void fr_bio_eof(fr_bio_t *bio)
Internal BIO function to run EOF callbacks.
Definition: base.c:244
int fr_bio_shutdown(fr_bio_t *bio)
Shut down a set of BIOs.
Definition: base.c:141
fr_bio_t * fr_bio_mem_sink_alloc(TALLOC_CTX *ctx, size_t read_size)
Allocate a memory buffer which sinks data from a bio system into the callers application.
Definition: mem.c:845
talloc_free(reap)
long int ssize_t
Definition: merged_model.c:24
static int fr_bio_pipe_eof(fr_bio_t *bio)
Set EOF.
Definition: pipe.c:143
static ssize_t fr_bio_pipe_read(fr_bio_t *bio, void *packet_ctx, void *buffer, size_t size)
Read from the pipe.
Definition: pipe.c:59
bool eof
are we at EOF?
Definition: pipe.c:40
fr_bio_t * fr_bio_pipe_alloc(TALLOC_CTX *ctx, fr_bio_pipe_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:172
static void fr_bio_pipe_shutdown(fr_bio_t *bio)
Shutdown callback.
Definition: pipe.c:127
fr_bio_pipe_cb_funcs_t signal
inform us that the pipe is readable
Definition: pipe.c:42
pthread_mutex_t mutex
Definition: pipe.c:44
FR_BIO_COMMON
Definition: pipe.c:36
static ssize_t fr_bio_pipe_write(fr_bio_t *bio, void *packet_ctx, void const *buffer, size_t size)
Write to the pipe.
Definition: pipe.c:96
fr_bio_t * next
Definition: pipe.c:38
static int fr_bio_pipe_destructor(fr_bio_pipe_t *my)
Definition: pipe.c:48
The pipe bio.
Definition: pipe.c:35
fr_bio_callback_t writeable
Definition: pipe.h:29
fr_bio_callback_t readable
Definition: pipe.h:28
fr_assert(0)