libnl 1.1.4
cache_mngr.c
1/*
2 * lib/cache_mngr.c Cache Manager
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation version 2.1
7 * of the License.
8 *
9 * Copyright (c) 2003-2007 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup cache_mngt
14 * @defgroup cache_mngr Manager
15 * @brief Helps keeping caches up to date.
16 *
17 * The purpose of a cache manager is to keep track of caches and
18 * automatically receive event notifications to keep the caches
19 * up to date with the kernel state. Each manager has exactly one
20 * netlink socket assigned which limits the scope of each manager
21 * to exactly one netlink family. Therefore all caches committed
22 * to a manager must be part of the same netlink family. Due to the
23 * nature of a manager, it is not possible to have a cache maintain
24 * two instances of the same cache type. The socket is subscribed
25 * to the event notification group of each cache and also put into
26 * non-blocking mode. Functions exist to poll() on the socket to
27 * wait for new events to be received.
28 *
29 * @code
30 * App libnl Kernel
31 * | |
32 * +-----------------+ [ notification, link change ]
33 * | | Cache Manager | | [ (IFF_UP | IFF_RUNNING) ]
34 * | | |
35 * | | +------------+| | | [ notification, new addr ]
36 * <-------|---| route/link |<-------(async)--+ [ 10.0.1.1/32 dev eth1 ]
37 * | | +------------+| | |
38 * | +------------+| |
39 * <---|---|---| route/addr |<------|-(async)--------------+
40 * | +------------+|
41 * | | +------------+| |
42 * <-------|---| ... ||
43 * | | +------------+| |
44 * +-----------------+
45 * | |
46 * @endcode
47 *
48 * @par 1) Creating a new cache manager
49 * @code
50 * struct nl_cache_mngr *mngr;
51 *
52 * // Allocate a new cache manager for RTNETLINK and automatically
53 * // provide the caches added to the manager.
54 * mngr = nl_cache_mngr_alloc(NETLINK_ROUTE, NL_AUTO_PROVIDE);
55 * @endcode
56 *
57 * @par 2) Keep track of a cache
58 * @code
59 * struct nl_cache *cache;
60 *
61 * // Create a new cache for links/interfaces and ask the manager to
62 * // keep it up to date for us. This will trigger a full dump request
63 * // to initially fill the cache.
64 * cache = nl_cache_mngr_add(mngr, "route/link");
65 * @endcode
66 *
67 * @par 3) Make the manager receive updates
68 * @code
69 * // Give the manager the ability to receive updates, will call poll()
70 * // with a timeout of 5 seconds.
71 * if (nl_cache_mngr_poll(mngr, 5000) > 0) {
72 * // Manager received at least one update, dump cache?
73 * nl_cache_dump(cache, ...);
74 * }
75 * @endcode
76 *
77 * @par 4) Release cache manager
78 * @code
79 * nl_cache_mngr_free(mngr);
80 * @endcode
81 * @{
82 */
83
84#include <netlink-local.h>
85#include <netlink/netlink.h>
86#include <netlink/cache.h>
87#include <netlink/utils.h>
88
89static int include_cb(struct nl_object *obj, struct nl_parser_param *p)
90{
91 struct nl_cache_assoc *ca = p->pp_arg;
92
93 NL_DBG(2, "Including object %p into cache %p\n", obj, ca->ca_cache);
94#ifdef NL_DEBUG
95 if (nl_debug >= 4)
96 nl_object_dump(obj, &nl_debug_dp);
97#endif
98 return nl_cache_include(ca->ca_cache, obj, ca->ca_change);
99}
100
101static int event_input(struct nl_msg *msg, void *arg)
102{
103 struct nl_cache_mngr *mngr = arg;
104 int protocol = nlmsg_get_proto(msg);
105 int type = nlmsg_hdr(msg)->nlmsg_type;
106 struct nl_cache_ops *ops;
107 int i, n;
108 struct nl_parser_param p = {
109 .pp_cb = include_cb,
110 };
111
112 NL_DBG(2, "Cache manager %p, handling new message %p as event\n",
113 mngr, msg);
114#ifdef NL_DEBUG
115 if (nl_debug >= 4)
116 nl_msg_dump(msg, stderr);
117#endif
118
119 if (mngr->cm_protocol != protocol)
120 BUG();
121
122 for (i = 0; i < mngr->cm_nassocs; i++) {
123 if (mngr->cm_assocs[i].ca_cache) {
124 ops = mngr->cm_assocs[i].ca_cache->c_ops;
125 for (n = 0; ops->co_msgtypes[n].mt_id >= 0; n++)
126 if (ops->co_msgtypes[n].mt_id == type)
127 goto found;
128 }
129 }
130
131 return NL_SKIP;
132
133found:
134 NL_DBG(2, "Associated message %p to cache %p\n",
135 msg, mngr->cm_assocs[i].ca_cache);
136 p.pp_arg = &mngr->cm_assocs[i];
137
138 return nl_cache_parse(ops, NULL, nlmsg_hdr(msg), &p);
139}
140
141/**
142 * Allocate new cache manager
143 * @arg handle Netlink socket/handle to be used
144 * @arg protocol Netlink Protocol this manager is used for
145 * @arg flags Flags
146 *
147 * @return Newly allocated cache manager or NULL on failure.
148 */
149struct nl_cache_mngr *nl_cache_mngr_alloc(struct nl_handle *handle,
150 int protocol, int flags)
151{
152 struct nl_cache_mngr *mngr;
153
154 if (handle == NULL)
155 BUG();
156
157 mngr = calloc(1, sizeof(*mngr));
158 if (!mngr)
159 goto enomem;
160
161 mngr->cm_handle = handle;
162 mngr->cm_nassocs = 32;
163 mngr->cm_protocol = protocol;
164 mngr->cm_flags = flags;
165 mngr->cm_assocs = calloc(mngr->cm_nassocs,
166 sizeof(struct nl_cache_assoc));
167 if (!mngr->cm_assocs)
168 goto enomem;
169
170
172 event_input, mngr);
173
174 /* Required to receive async event notifications */
175 nl_disable_sequence_check(mngr->cm_handle);
176
177 if (nl_connect(mngr->cm_handle, protocol) < 0)
178 goto errout;
179
180 if (nl_socket_set_nonblocking(mngr->cm_handle) < 0)
181 goto errout;
182
183 NL_DBG(1, "Allocated cache manager %p, protocol %d, %d caches\n",
184 mngr, protocol, mngr->cm_nassocs);
185
186 return mngr;
187
188enomem:
189 nl_errno(ENOMEM);
190errout:
191 nl_cache_mngr_free(mngr);
192 return NULL;
193}
194
195/**
196 * Add cache responsibility to cache manager
197 * @arg mngr Cache manager.
198 * @arg name Name of cache to keep track of
199 * @arg cb Function to be called upon changes.
200 *
201 * Allocates a new cache of the specified type and adds it to the manager.
202 * The operation will trigger a full dump request from the kernel to
203 * initially fill the contents of the cache. The manager will subscribe
204 * to the notification group of the cache to keep track of any further
205 * changes.
206 *
207 * @return The newly allocated cache or NULL on failure.
208 */
209struct nl_cache *nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name,
210 change_func_t cb)
211{
212 struct nl_cache_ops *ops;
213 struct nl_cache *cache;
214 struct nl_af_group *grp;
215 int err, i;
216
217 ops = nl_cache_ops_lookup(name);
218 if (!ops) {
219 nl_error(ENOENT, "Unknown cache type");
220 return NULL;
221 }
222
223 if (ops->co_protocol != mngr->cm_protocol) {
224 nl_error(EINVAL, "Netlink protocol mismatch");
225 return NULL;
226 }
227
228 if (ops->co_groups == NULL) {
229 nl_error(EOPNOTSUPP, NULL);
230 return NULL;
231 }
232
233 for (i = 0; i < mngr->cm_nassocs; i++) {
234 if (mngr->cm_assocs[i].ca_cache &&
235 mngr->cm_assocs[i].ca_cache->c_ops == ops) {
236 nl_error(EEXIST, "Cache of this type already managed");
237 return NULL;
238 }
239 }
240
241retry:
242 for (i = 0; i < mngr->cm_nassocs; i++)
243 if (!mngr->cm_assocs[i].ca_cache)
244 break;
245
246 if (i >= mngr->cm_nassocs) {
247 mngr->cm_nassocs += 16;
248 mngr->cm_assocs = realloc(mngr->cm_assocs,
249 mngr->cm_nassocs *
250 sizeof(struct nl_cache_assoc));
251 if (mngr->cm_assocs == NULL) {
252 nl_errno(ENOMEM);
253 return NULL;
254 } else {
255 NL_DBG(1, "Increased capacity of cache manager %p " \
256 "to %d\n", mngr, mngr->cm_nassocs);
257 goto retry;
258 }
259 }
260
261 cache = nl_cache_alloc(ops);
262 if (!cache) {
263 nl_errno(ENOMEM);
264 return NULL;
265 }
266
267 for (grp = ops->co_groups; grp->ag_group; grp++) {
268 err = nl_socket_add_membership(mngr->cm_handle, grp->ag_group);
269 if (err < 0)
270 goto errout_free_cache;
271 }
272
273 err = nl_cache_refill(mngr->cm_handle, cache);
274 if (err < 0)
275 goto errout_drop_membership;
276
277 mngr->cm_assocs[i].ca_cache = cache;
278 mngr->cm_assocs[i].ca_change = cb;
279
280 if (mngr->cm_flags & NL_AUTO_PROVIDE)
282
283 NL_DBG(1, "Added cache %p <%s> to cache manager %p\n",
284 cache, nl_cache_name(cache), mngr);
285
286 return cache;
287
288errout_drop_membership:
289 for (grp = ops->co_groups; grp->ag_group; grp++)
290 nl_socket_drop_membership(mngr->cm_handle, grp->ag_group);
291errout_free_cache:
292 nl_cache_free(cache);
293
294 return NULL;
295}
296
297/**
298 * Get file descriptor
299 * @arg mngr Cache Manager
300 *
301 * Get the file descriptor of the socket associated to the manager.
302 * This can be used to change socket options or monitor activity
303 * using poll()/select().
304 */
305int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
306{
307 return nl_socket_get_fd(mngr->cm_handle);
308}
309
310/**
311 * Check for event notifications
312 * @arg mngr Cache Manager
313 * @arg timeout Upper limit poll() will block, in milliseconds.
314 *
315 * Causes poll() to be called to check for new event notifications
316 * being available. Automatically receives and handles available
317 * notifications.
318 *
319 * This functionally is ideally called regularly during an idle
320 * period.
321 *
322 * @return A positive value if at least one update was handled, 0
323 * for none, or a negative error code.
324 */
325int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
326{
327 int ret;
328 struct pollfd fds = {
329 .fd = nl_socket_get_fd(mngr->cm_handle),
330 .events = POLLIN,
331 };
332
333 NL_DBG(3, "Cache manager %p, poll() fd %d\n", mngr, fds.fd);
334 ret = poll(&fds, 1, timeout);
335 NL_DBG(3, "Cache manager %p, poll() returned %d\n", mngr, ret);
336 if (ret < 0)
337 return nl_errno(errno);
338
339 if (ret == 0)
340 return 0;
341
342 return nl_cache_mngr_data_ready(mngr);
343}
344
345/**
346 * Receive available event notifications
347 * @arg mngr Cache manager
348 *
349 * This function can be called if the socket associated to the manager
350 * contains updates to be received. This function should not be used
351 * if nl_cache_mngr_poll() is used.
352 *
353 * @return A positive value if at least one update was handled, 0
354 * for none, or a negative error code.
355 */
356int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
357{
358 int err;
359
360 err = nl_recvmsgs_default(mngr->cm_handle);
361 if (err < 0)
362 return err;
363
364 return 1;
365}
366
367/**
368 * Free cache manager
369 * @arg mngr Cache manager
370 *
371 * Release all resources after usage of a cache manager.
372 */
373void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
374{
375 if (!mngr)
376 return;
377
378 if (mngr->cm_handle) {
379 nl_close(mngr->cm_handle);
380 nl_handle_destroy(mngr->cm_handle);
381 }
382
383 free(mngr->cm_assocs);
384 free(mngr);
385
386 NL_DBG(1, "Cache manager %p freed\n", mngr);
387}
388
389/** @} */
void nl_cache_mngr_free(struct nl_cache_mngr *mngr)
Free cache manager.
Definition: cache_mngr.c:373
struct nl_cache_mngr * nl_cache_mngr_alloc(struct nl_handle *handle, int protocol, int flags)
Allocate new cache manager.
Definition: cache_mngr.c:149
int nl_cache_mngr_poll(struct nl_cache_mngr *mngr, int timeout)
Check for event notifications.
Definition: cache_mngr.c:325
struct nl_cache * nl_cache_mngr_add(struct nl_cache_mngr *mngr, const char *name, change_func_t cb)
Add cache responsibility to cache manager.
Definition: cache_mngr.c:209
int nl_cache_mngr_get_fd(struct nl_cache_mngr *mngr)
Get file descriptor.
Definition: cache_mngr.c:305
int nl_cache_mngr_data_ready(struct nl_cache_mngr *mngr)
Receive available event notifications.
Definition: cache_mngr.c:356
struct nl_cache_ops * nl_cache_ops_lookup(const char *name)
Lookup cache operations by name.
Definition: cache_mngt.c:68
void nl_cache_mngt_provide(struct nl_cache *cache)
Provide a cache for global use.
Definition: cache_mngt.c:304
void nl_cache_free(struct nl_cache *cache)
Free a cache.
Definition: cache.c:277
struct nl_cache * nl_cache_alloc(struct nl_cache_ops *ops)
Allocate an empty cache.
Definition: cache.c:170
int nl_cache_refill(struct nl_handle *handle, struct nl_cache *cache)
(Re)fill a cache with the contents in the kernel.
Definition: cache.c:680
@ NL_SKIP
Skip this message.
Definition: handlers.h:63
@ NL_CB_VALID
Message is valid.
Definition: handlers.h:96
@ NL_CB_CUSTOM
Customized handler specified by the user.
Definition: handlers.h:84
void nl_msg_dump(struct nl_msg *msg, FILE *ofd)
Dump message in human readable format to file descriptor.
Definition: msg.c:947
struct nlmsghdr * nlmsg_hdr(struct nl_msg *n)
Return actual netlink message.
Definition: msg.c:643
void nl_close(struct nl_handle *handle)
Close/Disconnect netlink socket.
Definition: nl.c:247
int nl_connect(struct nl_handle *handle, int protocol)
Create and connect netlink socket.
Definition: nl.c:191
int nl_recvmsgs_default(struct nl_handle *handle)
Receive a set of message from a netlink socket using handlers in nl_handle.
Definition: nl.c:782
void nl_object_dump(struct nl_object *obj, struct nl_dump_params *params)
Dump this object according to the specified parameters.
Definition: object.c:251
int nl_socket_drop_membership(struct nl_handle *handle, int group)
Leave a group.
Definition: socket.c:388
void nl_disable_sequence_check(struct nl_handle *handle)
Disable sequence number checking.
Definition: socket.c:285
int nl_socket_add_membership(struct nl_handle *handle, int group)
Join a group.
Definition: socket.c:361
int nl_socket_modify_cb(struct nl_handle *handle, enum nl_cb_type type, enum nl_cb_kind kind, nl_recvmsg_msg_cb_t func, void *arg)
Modify the callback handler associated to the socket.
Definition: socket.c:514
void nl_handle_destroy(struct nl_handle *handle)
Destroy netlink handle.
Definition: socket.c:246
int nl_socket_set_nonblocking(struct nl_handle *handle)
Set file descriptor of socket handle to non-blocking state.
Definition: socket.c:454
int nl_debug
Debug level.
Definition: utils.c:25
Address family to netlink group association.
Definition: cache-api.h:144
int ag_group
Netlink group identifier.
Definition: cache-api.h:149
Cache Operations.
Definition: cache-api.h:164
int mt_id
Netlink message type.
Definition: cache-api.h:131
uint16_t nlmsg_type
Message type (content type)