]> git.proxmox.com Git - mirror_frr.git/blob - lib/frr_zmq.c
Merge pull request #9759 from opensourcerouting/workflow-dev-tag
[mirror_frr.git] / lib / frr_zmq.c
1 /*
2 * libzebra ZeroMQ bindings
3 * Copyright (C) 2015 David Lamparter
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the Free
7 * Software Foundation; either version 2 of the License, or (at your option)
8 * any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; see the file COPYING; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18 */
19
20 /*
21 * IF YOU MODIFY THIS FILE PLEASE RUN `make check` and ensure that
22 * the test_zmq.c unit test is still working. There are dependencies
23 * between the two that are extremely fragile. My understanding
24 * is that there is specialized ownership of the cb pointer based
25 * upon what is happening. Those assumptions are supposed to be
26 * tested in the test_zmq.c
27 */
28 #include <zebra.h>
29 #include <zmq.h>
30
31 #include "thread.h"
32 #include "memory.h"
33 #include "frr_zmq.h"
34 #include "log.h"
35 #include "lib_errors.h"
36
37 DEFINE_MTYPE_STATIC(LIB, ZEROMQ_CB, "ZeroMQ callback");
38
39 /* libzmq's context */
40 void *frrzmq_context = NULL;
41 static unsigned frrzmq_initcount = 0;
42
43 void frrzmq_init(void)
44 {
45 if (frrzmq_initcount++ == 0) {
46 frrzmq_context = zmq_ctx_new();
47 zmq_ctx_set(frrzmq_context, ZMQ_IPV6, 1);
48 }
49 }
50
51 void frrzmq_finish(void)
52 {
53 if (--frrzmq_initcount == 0) {
54 zmq_ctx_term(frrzmq_context);
55 frrzmq_context = NULL;
56 }
57 }
58
59 static int frrzmq_read_msg(struct thread *t)
60 {
61 struct frrzmq_cb **cbp = THREAD_ARG(t);
62 struct frrzmq_cb *cb;
63 zmq_msg_t msg;
64 unsigned partno;
65 unsigned char read = 0;
66 int ret, more;
67 size_t moresz;
68
69 if (!cbp)
70 return 1;
71 cb = (*cbp);
72 if (!cb || !cb->zmqsock)
73 return 1;
74
75 while (1) {
76 zmq_pollitem_t polli = {.socket = cb->zmqsock,
77 .events = ZMQ_POLLIN};
78 ret = zmq_poll(&polli, 1, 0);
79
80 if (ret < 0)
81 goto out_err;
82
83 if (!(polli.revents & ZMQ_POLLIN))
84 break;
85
86 if (cb->read.cb_msg) {
87 cb->in_cb = true;
88 cb->read.cb_msg(cb->read.arg, cb->zmqsock);
89 cb->in_cb = false;
90
91 read = 1;
92
93 if (cb->read.cancelled) {
94 frrzmq_check_events(cbp, &cb->write,
95 ZMQ_POLLOUT);
96 cb->read.thread = NULL;
97 if (cb->write.cancelled && !cb->write.thread)
98 XFREE(MTYPE_ZEROMQ_CB, *cbp);
99
100 return 0;
101 }
102 continue;
103 }
104
105 partno = 0;
106 if (zmq_msg_init(&msg))
107 goto out_err;
108 do {
109 ret = zmq_msg_recv(&msg, cb->zmqsock, ZMQ_NOBLOCK);
110 if (ret < 0) {
111 if (errno == EAGAIN)
112 break;
113
114 zmq_msg_close(&msg);
115 goto out_err;
116 }
117 read = 1;
118
119 cb->in_cb = true;
120 cb->read.cb_part(cb->read.arg, cb->zmqsock, &msg,
121 partno);
122 cb->in_cb = false;
123
124 if (cb->read.cancelled) {
125 zmq_msg_close(&msg);
126 frrzmq_check_events(cbp, &cb->write,
127 ZMQ_POLLOUT);
128 cb->read.thread = NULL;
129 if (cb->write.cancelled && !cb->write.thread)
130 XFREE(MTYPE_ZEROMQ_CB, *cbp);
131
132 return 0;
133 }
134
135 /* cb_part may have read additional parts of the
136 * message; don't use zmq_msg_more here */
137 moresz = sizeof(more);
138 more = 0;
139 ret = zmq_getsockopt(cb->zmqsock, ZMQ_RCVMORE, &more,
140 &moresz);
141 if (ret < 0) {
142 zmq_msg_close(&msg);
143 goto out_err;
144 }
145
146 partno++;
147 } while (more);
148 zmq_msg_close(&msg);
149 }
150
151 if (read)
152 frrzmq_check_events(cbp, &cb->write, ZMQ_POLLOUT);
153
154 thread_add_read(t->master, frrzmq_read_msg, cbp,
155 cb->fd, &cb->read.thread);
156 return 0;
157
158 out_err:
159 flog_err(EC_LIB_ZMQ, "ZeroMQ read error: %s(%d)", strerror(errno),
160 errno);
161 if (cb->read.cb_error)
162 cb->read.cb_error(cb->read.arg, cb->zmqsock);
163 return 1;
164 }
165
166 int _frrzmq_thread_add_read(const struct xref_threadsched *xref,
167 struct thread_master *master,
168 void (*msgfunc)(void *arg, void *zmqsock),
169 void (*partfunc)(void *arg, void *zmqsock,
170 zmq_msg_t *msg, unsigned partnum),
171 void (*errfunc)(void *arg, void *zmqsock),
172 void *arg, void *zmqsock,
173 struct frrzmq_cb **cbp)
174 {
175 int fd, events;
176 size_t len;
177 struct frrzmq_cb *cb;
178
179 if (!cbp)
180 return -1;
181 if (!(msgfunc || partfunc) || (msgfunc && partfunc))
182 return -1;
183 len = sizeof(fd);
184 if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len))
185 return -1;
186 len = sizeof(events);
187 if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len))
188 return -1;
189
190 if (*cbp)
191 cb = *cbp;
192 else {
193 cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
194 cb->write.cancelled = true;
195 *cbp = cb;
196 }
197
198 cb->zmqsock = zmqsock;
199 cb->fd = fd;
200 cb->read.arg = arg;
201 cb->read.cb_msg = msgfunc;
202 cb->read.cb_part = partfunc;
203 cb->read.cb_error = errfunc;
204 cb->read.cancelled = false;
205 cb->in_cb = false;
206
207 if (events & ZMQ_POLLIN) {
208 thread_cancel(&cb->read.thread);
209
210 thread_add_event(master, frrzmq_read_msg, cbp, fd,
211 &cb->read.thread);
212 } else
213 thread_add_read(master, frrzmq_read_msg, cbp, fd,
214 &cb->read.thread);
215 return 0;
216 }
217
218 static int frrzmq_write_msg(struct thread *t)
219 {
220 struct frrzmq_cb **cbp = THREAD_ARG(t);
221 struct frrzmq_cb *cb;
222 unsigned char written = 0;
223 int ret;
224
225 if (!cbp)
226 return 1;
227 cb = (*cbp);
228 if (!cb || !cb->zmqsock)
229 return 1;
230
231 while (1) {
232 zmq_pollitem_t polli = {.socket = cb->zmqsock,
233 .events = ZMQ_POLLOUT};
234 ret = zmq_poll(&polli, 1, 0);
235
236 if (ret < 0)
237 goto out_err;
238
239 if (!(polli.revents & ZMQ_POLLOUT))
240 break;
241
242 if (cb->write.cb_msg) {
243 cb->in_cb = true;
244 cb->write.cb_msg(cb->write.arg, cb->zmqsock);
245 cb->in_cb = false;
246
247 written = 1;
248
249 if (cb->write.cancelled) {
250 frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
251 cb->write.thread = NULL;
252 if (cb->read.cancelled && !cb->read.thread)
253 XFREE(MTYPE_ZEROMQ_CB, *cbp);
254
255 return 0;
256 }
257 continue;
258 }
259 }
260
261 if (written)
262 frrzmq_check_events(cbp, &cb->read, ZMQ_POLLIN);
263
264 thread_add_write(t->master, frrzmq_write_msg, cbp,
265 cb->fd, &cb->write.thread);
266 return 0;
267
268 out_err:
269 flog_err(EC_LIB_ZMQ, "ZeroMQ write error: %s(%d)", strerror(errno),
270 errno);
271 if (cb->write.cb_error)
272 cb->write.cb_error(cb->write.arg, cb->zmqsock);
273 return 1;
274 }
275
276 int _frrzmq_thread_add_write(const struct xref_threadsched *xref,
277 struct thread_master *master,
278 void (*msgfunc)(void *arg, void *zmqsock),
279 void (*errfunc)(void *arg, void *zmqsock),
280 void *arg, void *zmqsock, struct frrzmq_cb **cbp)
281 {
282 int fd, events;
283 size_t len;
284 struct frrzmq_cb *cb;
285
286 if (!cbp)
287 return -1;
288 if (!msgfunc)
289 return -1;
290 len = sizeof(fd);
291 if (zmq_getsockopt(zmqsock, ZMQ_FD, &fd, &len))
292 return -1;
293 len = sizeof(events);
294 if (zmq_getsockopt(zmqsock, ZMQ_EVENTS, &events, &len))
295 return -1;
296
297 if (*cbp)
298 cb = *cbp;
299 else {
300 cb = XCALLOC(MTYPE_ZEROMQ_CB, sizeof(struct frrzmq_cb));
301 cb->read.cancelled = true;
302 *cbp = cb;
303 }
304
305 cb->zmqsock = zmqsock;
306 cb->fd = fd;
307 cb->write.arg = arg;
308 cb->write.cb_msg = msgfunc;
309 cb->write.cb_part = NULL;
310 cb->write.cb_error = errfunc;
311 cb->write.cancelled = false;
312 cb->in_cb = false;
313
314 if (events & ZMQ_POLLOUT) {
315 thread_cancel(&cb->write.thread);
316
317 _thread_add_event(xref, master, frrzmq_write_msg, cbp, fd,
318 &cb->write.thread);
319 } else
320 thread_add_write(master, frrzmq_write_msg, cbp, fd,
321 &cb->write.thread);
322 return 0;
323 }
324
325 void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core)
326 {
327 if (!cb || !*cb)
328 return;
329 core->cancelled = true;
330 thread_cancel(&core->thread);
331
332 /* If cancelled from within a callback, don't try to free memory
333 * in this path.
334 */
335 if ((*cb)->in_cb)
336 return;
337
338 /* Ok to free the callback context if no more ... context. */
339 if ((*cb)->read.cancelled && !(*cb)->read.thread
340 && (*cb)->write.cancelled && ((*cb)->write.thread == NULL))
341 XFREE(MTYPE_ZEROMQ_CB, *cb);
342 }
343
344 void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core,
345 int event)
346 {
347 struct frrzmq_cb *cb;
348 int events;
349 size_t len;
350
351 if (!cbp)
352 return;
353 cb = (*cbp);
354 if (!cb || !cb->zmqsock)
355 return;
356
357 len = sizeof(events);
358 if (zmq_getsockopt(cb->zmqsock, ZMQ_EVENTS, &events, &len))
359 return;
360 if ((events & event) && core->thread && !core->cancelled) {
361 struct thread_master *tm = core->thread->master;
362
363 thread_cancel(&core->thread);
364
365 if (event == ZMQ_POLLIN)
366 thread_add_event(tm, frrzmq_read_msg,
367 cbp, cb->fd, &core->thread);
368 else
369 thread_add_event(tm, frrzmq_write_msg,
370 cbp, cb->fd, &core->thread);
371 }
372 }