summaryrefslogtreecommitdiffstats
path: root/lib/frr_zmq.h
blob: 73da3770f4b90717898df1724e3154d22104e56f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * libzebra ZeroMQ bindings
 * Copyright (C) 2015  David Lamparter
 */

#ifndef _FRRZMQ_H
#define _FRRZMQ_H

#include "frrevent.h"
#include <zmq.h>

#ifdef __cplusplus
extern "C" {
#endif

/* linking/packaging note:  this is a separate library that needs to be
 * linked into any daemon/library/module that wishes to use its
 * functionality.  The purpose of this is to encapsulate the libzmq
 * dependency and not make libfrr/FRR itself depend on libzmq.
 *
 * libfrrzmq should be put in LDFLAGS/LIBADD *before* either libfrr or
 * libzmq, and both of these should always be listed, e.g.
 *   foo_LDFLAGS = libfrrzmq.la libfrr.la $(ZEROMQ_LIBS)
 */

/* callback integration */
struct cb_core {
	struct event *thread;
	void *arg;

	bool cancelled;

	void (*cb_msg)(void *arg, void *zmqsock);
	void (*cb_part)(void *arg, void *zmqsock, zmq_msg_t *msg,
			unsigned partnum);
	void (*cb_error)(void *arg, void *zmqsock);
};

struct frrzmq_cb {
	void *zmqsock;
	int fd;

	bool in_cb; /* This context is in a read or write callback. */

	struct cb_core read;
	struct cb_core write;
};

/* libzmq's context
 *
 * this is mostly here as a convenience, it has IPv6 enabled but nothing
 * else is tied to it;  you can use a separate context without problems
 */
extern void *frrzmq_context;

extern void frrzmq_init(void);
extern void frrzmq_finish(void);

#define _xref_zmq_a(type, f, d, call)                                          \
	({                                                                     \
		static const struct xref_eventsched _xref __attribute__(       \
			(used)) = {                                            \
			.xref = XREF_INIT(XREFT_EVENTSCHED, NULL, __func__),   \
			.funcname = #f,                                        \
			.dest = #d,                                            \
			.event_type = EVENT_##type,                            \
		};                                                             \
		XREF_LINK(_xref.xref);                                         \
		call;                                                          \
	}) /* end */

/* core event registration, one of these 2 macros should be used */
#define frrzmq_event_add_read_msg(m, f, e, a, z, d)                            \
	_xref_zmq_a(READ, f, d,                                                \
		    _frrzmq_event_add_read(&_xref, m, f, NULL, e, a, z, d))

#define frrzmq_event_add_read_part(m, f, e, a, z, d)                           \
	_xref_zmq_a(READ, f, d,                                                \
		    _frrzmq_event_add_read(&_xref, m, NULL, f, e, a, z, d))

#define frrzmq_event_add_write_msg(m, f, e, a, z, d)                           \
	_xref_zmq_a(WRITE, f, d,                                               \
		    _frrzmq_event_add_write(&_xref, m, f, e, a, z, d))

struct cb_core;
struct frrzmq_cb;

/* Set up a POLLIN or POLLOUT notification to be called from the libfrr main
 * loop. This has the following properties:
 *
 * - since ZeroMQ works with edge triggered notifications, it will loop and
 *   dispatch as many events as ZeroMQ has pending at the time libfrr calls
 *   into this code
 * - due to this looping (which means it non-single-issue), the callback is
 *   also persistent.  Do _NOT_ re-register the event inside of your
 *   callback function.
 * - either msgfunc or partfunc will be called (only one can be specified)
 *   - msgfunc is called once for each incoming message
 *   - if partfunc is specified, the message is read and partfunc is called
 *     for each ZeroMQ multi-part subpart.  Note that you can't send replies
 *     before all parts have been read because that violates the ZeroMQ FSM.
 * - write version doesn't allow for partial callback, you must handle the
 *   whole message (all parts) in msgfunc callback
 * - you can safely cancel the callback from within itself
 * - installing a callback will check for pending events (ZMQ_EVENTS) and
 *   may schedule the event to run as soon as libfrr is back in its main
 *   loop.
 */
extern int
_frrzmq_event_add_read(const struct xref_eventsched *xref,
		       struct event_loop *master,
		       void (*msgfunc)(void *arg, void *zmqsock),
		       void (*partfunc)(void *arg, void *zmqsock,
					zmq_msg_t *msg, unsigned partnum),
		       void (*errfunc)(void *arg, void *zmqsock), void *arg,
		       void *zmqsock, struct frrzmq_cb **cb);
extern int _frrzmq_event_add_write(const struct xref_eventsched *xref,
				   struct event_loop *master,
				   void (*msgfunc)(void *arg, void *zmqsock),
				   void (*errfunc)(void *arg, void *zmqsock),
				   void *arg, void *zmqsock,
				   struct frrzmq_cb **cb);

extern void frrzmq_thread_cancel(struct frrzmq_cb **cb, struct cb_core *core);

/*
 * http://api.zeromq.org/4-2:zmq-getsockopt#toc10
 *
 * As the descriptor is edge triggered, applications must update the state of
 * ZMQ_EVENTS after each invocation of zmq_send or zmq_recv.To be more explicit:
 * after calling zmq_send the socket may become readable (and vice versa)
 * without triggering a read event on the file descriptor.
 */
extern void frrzmq_check_events(struct frrzmq_cb **cbp, struct cb_core *core,
				int event);

#ifdef __cplusplus
}
#endif

#endif /* _FRRZMQ_H */