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